• Можно ли вставить виджет на страницу сайта в React?

    nowm
    @nowm
    У Elfsight есть справка на этут тему.

    1. Установите react-elfsight-widget

    npm install react-elfsight-widget

    2. Используйте такой код:

    import React from 'react';
    import { ElfsightWidget } from 'react-elfsight-widget';
    
    function Component() {
      return <ElfsightWidget widgetID="хххххххх-хххх-хххх-хххх-хххххххххххх" />;
    }


    Соответственно, вместо хххххххх-хххх-хххх-хххх-хххххххххххх подставьте идентификатор вашего виджета.
    Ответ написан
  • Как считать измененный атрибут?

    nowm
    @nowm
    У jQuery есть проблемы с кэшированием в data. Я когда работал с ним, всегда старался делать что-то вроде container.attr('data-blocked'), чтобы не ловить самые неожиданные глюки.

    // Прочитать значение data-blocked
    container.attr('data-blocked');
    
    // Установить новое значение data-blocked
    container.attr('data-blocked', 'some value');
    Ответ написан
  • Как найти и вернуть записи в Mongodb?

    nowm
    @nowm
    Вот это должно быть параметром метода find: { "title": "статьи","text": "статьи"}. Но у вас в качестве критерия для запроса указан пустой объект (это значит, что выберутся все документы из коллекции), а в select указан дополнительный объект, который говорит, что нужно из результатов брать поля _id, title и text.

    Метод select в Mongoose, кстати, используется немного не так. Когда вы передаёте в него в качестве параметра объект, нужно значениям указывать либо 1 (поле включено в результат), либо 0 (поле не включается в результат). Если вы передаёте непустую строку в качестве значения, она воспринимается как 1, но это будет искажать ваше понимание того, что происходит. Select — это только указание, какие поля должны быть в результате, этот метод вообще никак не влияет на условие выборки документов из коллекции. Условия выборки указываются в find.
    Ответ написан
    4 комментария
  • SVG больше чем path в нём, как сравнять их размеры?

    nowm
    @nowm
    Размер SVG можно задать с помощью атрибута viewBox. Например, эта картинка имеет размер 160 на 90:
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 90">
    </svg>


    Кроме манипулирования размером самого SVG можно менять размер path без редактирования самого пути. Это делается с помощью атрибута transform. Например, на следующей картинке все 3 path прописаны под размер SVG, но из-за применяемого масштабирования красная область занимает 80%, зелёная — 40%, а синяя — 20%.

    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 90">
        <path d="M0 0L160 0L160 90L0 90L0 0" fill="#ff0000" transform="scale(0.8)"/>
        <path d="M0 0L160 0L160 90L0 90L0 0" fill="#00ff00" transform="scale(0.4)"/>
        <path d="M0 0L160 0L160 90L0 90L0 0" fill="#0000ff" transform="scale(0.2)"/>
    </svg>
    Ответ написан
  • Apache и кириллица?

    nowm
    @nowm
    Судя по скриншоту в комментариях к вопросу, у вас файл в кодировке UTF-8.

    В Apache

    Первый способ: в файле httpd.conf самого апача, либо в файле .htaccess, который находится в той же папке, что и HTML-файл (если его там нет, то создайте) можно прописать такую директиву:

    AddDefaultCharset utf-8

    Или так (если кодировку нужно устанавливать только для HTML-файлов):

    AddCharset utf-8 .html

    В HTML

    Второй способ: в самом HTML-файле в блоке <head> можно добавить тег, который скажет браузеру, что это UTF-8.

    <meta charset="UTF-8"/>
    Ответ написан
    Комментировать
  • Как прапвильно использовать Document.getElementsByClassName?

    nowm
    @nowm
    Метод getElementById возвращает один элемент, а метод getElementsByClassName возвращает HTMLCollection.

    Соответственно, нужно сначала из HTMLCollection получить элементы, и уже этим элементам задавать свойство hidden

    function showForm(className) {
        const elements = document.getElementsByClassName(className);
    
        for (let index = 0; index < elements.length; index++) {
            elements[index].hidden = false;
        }
    }
    Ответ написан
  • Как оповестить создателя о начале работы модератора?

    nowm
    @nowm
    Ваши вопросы, скорее всего, удаляют из-за того, что они не конкретные. Вы не пытались самостотельно ничего делать, не натолкнулись в процессе на ошибку, которую не знаете как пофиксить. Вместо этого вы говорите, что у вас есть непонятные функции /work start и /work stop (у вопроса тег «Java», но такие функции в нём синтаксис не позволит создать, значит речь о чём-то другом, а не о Java).

    Дальше, непонятно, кто такая администрация, кто такие модераторы и кто создатель. Такая информация зашивается в ваш плагин или хранится в какой-то базе данных?

    Что значит «оповестить создателя»? Написать ему сообщение в Телеграме, отправить письмо на электропочту, показать всплывающее окошко на сайте или в реальной жизни передать записку?

    И ещё куча таких вопросов. Все они возникают из-за того, что вы не конкретизируете вопрос, надеясь что у всех работает телепатия и они могут узнать детали из ваших мыслей.
    Ответ написан
    1 комментарий
  • Как работает одновременно асинхронное чтение и запись из примера boost.asio?

    nowm
    @nowm
    async_read не перекрывает async_write потому, что под капотом происходит не совсем то, что вы видите в исходных кодах. Блоки кода вроде boost::asio::async_read и boost::asio::async_write (я их далее буду называть задачами) во время компиляции разбиваются на более мелкие инструкции. В фоне система может приостанавливать исполнение одной задачи, запоминая все её данные, переключаться на другую задачу и какое-то время исполнять её, потом приостанавливать её исполнение и переключаться на третью задачу, потом снова на первую и так далее по кругу. За счёт этого можно в рамках одного потока выполнять сразу несколько задач. Сама эта система конфигурируется таким образом, чтобы переключаться по очереди этих задач один раз, например, в микросекунду. И таким образом она кусками их выполняет, а у вас складывается впечатление, что они работают параллельно, потому что переключения по очереди происходят очень быстро и зависят не от объёма кода, который нужно выполнить, а от фиксированного времени, которое система готова потратить на работу с одной задачей.

    Обычно асинхронность именно так и организуется. Если же исполнение синхронное, то система просто работает над одной задачей и никуда не переключается, пока её не завершит.

    Не уверен, что правильно понял ваш второй вопрос. Когда операции выполняются, они естественно потребляют ресурсы. А если их нет в очереди исполнения, то никаких ресурсов не потребляется. Система просто вхолостую раз в микросекунду проверяет очередь задач.

    Read время от времени запускается, так что да, определённые ресурсы потребляются, потому что всегда проверяется, если что-нибудь, что нужно прочитать, но там проверяется буффер на предмет данных, а не сам сокет. Так что, ресурсы потребляются, но не настолько сильно, как при чтении существующих данных.

    (Раз в микросекунду — это не точное значение, и я его привёл только для примера, чтобы было понятно, что переключение по очереди задач происходит очень часто.)
    Ответ написан
    2 комментария
  • Как реализовать авторизацию/аутентификацию с помощью access/refresh tokens с использованием JWT?

    nowm
    @nowm
    3. Оба ли эти токены будут являться JWT?

    JWT — это строка, сформированная в соответствие с RFC7519. Если формат правильный, это JWT, если нет — это не JWT. Если вы распечатаете JWT-токен на бумаге и используете её как кулёк для семечек, эти токены не перестанут быть JWT-токенами. То есть, область применения никак не влияет на то, JWT это или нет — только формат самого токена.

    ---

    Суть Access- и Refresh-токенов в том, что у вас может быть 10 разных серверов, один из которых используется для авторизации. На сервере авторизации вы генерируете токены и сообщаете их клиенту, каким-то способом, безопасность и удобство которого вас устраивает. Кто-то в куках передаёт, кто-то прямыми ответами сервера. Остальные 9 серверов принимают запросы, в которых указаны Access-токены. Тут тоже можно разными способами это делать. Можно в GET-параметрах токен передавать, можно в куках, можно в теле запроса, можно использовать хэдер Authorization — опять же, это на ваше усмотрение, в зависимости от того, какой способ вам кажется более удобным или более безопасным. На этих 9 серверах есть открытые ключи авторизационного сервера либо они могут обратиться к авторизационному серверу за ними. Используя эти открытые ключи, они проверяют Access-токен на валиднось, и если всё хорошо, выполняют запрос, в котором был этот токен, вытаскивая из токена, например, идентификатор текущего пользователя и используя его дальше. Сами эти 9 серверов не занимаются генерацией токенов вообще. Они только проверяют их. Соответственно, им не требуется хранить в БД или в кэше конкретные токены, чтобы удостоверяться в легитимности запроса, им нужен только открытый ключ авторизационного сервера, закрытой частью которого он подписывает эти токены. Это упрощает на их стороне логику, и они могут сосредоточиться на своих узких задачах.

    Авторизационный сервер занимается только генерацией токенов. Именно он решает, что, например, Access-токен должен жить 3 минуты, а Refresh-токен — 30 дней. Он делает свои личные проверки любых данных, которые может достать (IP, UA, идентификатор сессии и так далее) — вы сами решаете и сами пишете логику того, какие проверки нужно делать и как. Если вы считаете, что в полнолуние нужно отказывать в генерации токена для браузера Firefox пользователям из Кении, именно так и программируйте систему, потому что за вас библиотека JWT автоматом такие проверки делать не будет — это не её назначение. Её назначение проверять и создавать JWT-токены из данных, которые вы ей даёте на входе. Среди этих данных могут быть идентификаторы пользователя, сессии и т.п., если вы сами захотели их туда засунуть.

    Когда вы запрашиваете новый Access-токен, передавая в параметрах Refresh-токен (опять же, передавать его можно разными способами в зависимости от ваших вкусов и того, как вы запрограммировали сервер), авторизационный сервер проверяет его валидность и принимает решение, выдавать новый токен или нет. Вполне можно построить систему так, чтобы Refresh-токен не приходилось хранить в БД на стороне сервера авторизации, потому что все нужные данные можно поместить прямо в payload токена — он всё равно подписывается, так что если клиент подменит там идентификатор пользователя, токен не пройдёт валидацию.

    Фишка с Access- и Refresh-токенами нужна для того, чтобы организовать распределённую работу нескольких серверов. Если вы генерируете токены на сервере, а потом на этом же сервере принимаете запросы, в которых используется Access-токен, вы делаете что-то неправильно, потому что в таких ситуациях городить подобную архитектуру не нужно, и передавать Refresh-токен вместе с Access-токеном API-серверу тоже не нужно, потому что генерация новых токенов — это не его забота.

    Немного выше я писал, что токены со стороны клиента можно передавать по-разному в зависимости от предпочтений: в куках, в GET-параметрах, в теле запроса или в хэдере Authorization. Хорошая практика — это когда вы передаёте их в хэдере Authorization и больше никак. Делаете запрос к API-серверу? В хэдер помещается Access-токен. Делаете запрос на продление токена к авторизационному серверу? В хэдер помещается Refresh-токен. Всё просто и однотипно.
    Ответ написан
    1 комментарий
  • Стоит ли постоянно использовать css файлы для стилей в React?

    nowm
    @nowm
    Мне нравится вариант с отдельными CSS-файлами или описанием стилей с помощью тега <style>, потому что с их помощью можно работать с псевдо-селекторами, вроде «hover», «before», «after» и так далее. Так же, если использовать методологию БЭМ или подход из Twitter Bootstrap, можно значительно упростить вещи, и тогда вы в JS-коде будете писать логику работы приложения, а не решать вопросы дизайна.

    Плагины, вроде Radium и ему подобных, заставляют писать больше кода и добавляют новые зависимости в проект. Чем меньше кода и зависимостей, тем меньше потенциальных точек отказа и проще обслуживать и развивать приложение.
    Ответ написан
    Комментировать
  • Что из себя представляет интернет соединение?

    nowm
    @nowm
    Самая ближайшая аналогия: вы открываете чат с другом и болтаете минут 20. Несмотря на то, что вы отправляли друг другу много сообщений, все они отправлялись в рамках одного разговора, и вы их считатете связанными. Они были отправлены в рамках этого непрерывного соединения-разговора. «Под капотом» в это время может происходить много разных вещей. Например, чтобы написать сообщение, нужно достать телефон из кармана, разблокировать экран, открыть приложение-чат, выбрать собеседника, кликнуть на поле ввода сообщения, по-очереди нажимать на буквы экранной клавиатуры. Телефон, тоже где-то там под капотом делает много вещей, чтобы обеспечить вам возможность разговора с другом. Но эти детали вас не отвлекают, и вы считаете, что вы просто разговариваете с другом. У вас непрерывное 20-минутное соединение-разговор, даже если вы за эти 20 минут несколько раз доставали телефон и клали обратно.

    То же самое можно сказать про непрерывное соединение клиента и сервера. Оно состоит из отдельных сообщений и кучи телодвижений под капотом, но абстрактно воспринимается как одно соединение, потому что все эти мелкие сопутствующие телодвижения не являются существенными. Но, по сути, на самом низком уровне просто происходит периодический обмен сигналами и их обработка. Никакой непрерывности там и в помине нет.
    Ответ написан
    Комментировать
  • Как вставить изображение из input в img?

    nowm
    @nowm
    Чтобы вставить изображение из input type="file", нужно после загрузки изображения прочитать содержимое этого файла с помощью FileReader, сконвертировать в формат data url и только потом это применять к тегу img.

    myImg.onchange = function(event) {
        var target = event.target;
    
        if (!FileReader) {
            alert('FileReader не поддерживается — облом');
            return;
        }
    
        if (!target.files.length) {
            alert('Ничего не загружено');
            return;
        }
    
        var fileReader = new FileReader();
        fileReader.onload = functino() {
            img1.src = fileReader.result;
        }
    
        fileReader.readAsDataURL(target.files[0]);
    }


    В вашем коде myImg — это input type="file". У таких инпутов нет src. Когда с их помощью подгружаются файлы, в свойстве files, находятся все загруженные в этот инпут файлы. Точнее не файлы, а специальные объекты-интерфейсы, привязанные к загруженным файлам. С помощью этих интерфейсов уже можно делать различные действия с соответствующими файлами: узнавать название файла, дату последнего изменения, MIME-тип файла, читать его содержимое и так далее.
    Ответ написан
    7 комментариев
  • В чем проблема слэш команд?

    nowm
    @nowm
    У дискорда есть такая фишка, что на interaction нужно ответить в течение 3-х секунд. Если не успели, он начинает выдавать такую ошибку. Есть несколько вариантов:

    1. Сразу после того, как был получен interaction, нужно вызвать interaction.deferReply. Вызов deferReply даёт возможность продлить срок ответа до 15 минут. Дальше как обычно — делаете pool.query и потом interaction.reply
      await interaction.deferReply();
      // После вызова кода выше, дискорд даёт ещё 15 минут 
      // на то, чтобы отправить ответ.
      
      // [Тут какой-то ваш код, который долго работает]
      
      await interaction.reply('Информация о сервере: бла-блабла');


    2. Можно использовать механизм Follow-ups. В таком случае вы сразу что-нибудь отвечаете с помощью interaction.reply, потом делаете pool.query и потом добавляете ещё текст с помощью interaction.followUp:
      await interaction.reply('Ща, погодь, нужно в БД посмотреть...');
      // После вызова кода выше, дискорд даёт ещё 15 минут 
      // на то, чтобы отправить follow up.
      
      // [Тут какой-то ваш код, который долго работает]
      
      await interaction.followUp('Вот, нашёл: Информация о сервере: бла-блабла');
      // Код выше добавляет к ответу текст "Вот, нашёл: Информация о сервере: бла-блабла" 
      // (фраза «Ща, погодь» никуда не пропадает)


    3. Можно точно так же сразу ответить, но вместо interaction.followUp вызывать interaction.editReply. В этом случае даётся тоже 15 минут на то, чтобы вызвать editReply.
      await interaction.reply('Подождите...');
      // После вызова кода выше, дискорд даёт ещё 15 минут 
      // на то, чтобы отредактировать это сообщение
      
      // [Тут какой-то ваш код, который долго работает]
      
      await interaction.editReply('Информация о сервере: бла-блабла');
      // Код выше заменяет "Подождите.." на "Информация о сервере: бла-блабла"


    Ответ написан
    Комментировать
  • Как работает node.js сервер?

    nowm
    @nowm
    Команда node — это интерпретатор. Сам NodeJS — это не сервер, а среда исполнения, которая, в числе прочего, даёт возможность писать на JS web-серверы, используя встроенные модули NodeJS. Для того, чтобы сделать web-сервер, нужно написать такой файл test.js, в котором будет запускаться сервер с определёнными параметрами и определённым портом. Затем этот сервер запускается командой node test.js. Если вы будете напрямую открывать файл test.js из браузера, это не сработает так, как вы хотите, так что да — он вообще не должен быть в public_html.

    Пример test.js.

    // Импортируется встроенный в NodeJS модуль http,
    // который даёт возможность создать web-сервер
    const http = require('http');
    
    /**
     * Когда делается запрос к этому серверу, вызывается эта функция
     *
     * @param {module:http.IncomingMessage} request
     * @param {module:http.ServerResponse} response
     */
    function requestProcessor(request, response) {
        switch (request.url) {
            case '/':
                response.end('Запрошена главная страница');
                return;
            case '/500':
                response.statusCode = 500;
                response.end('По просьбам посетителей — ошибка 500');
                return;
        }
    
        response.end(`Не знаю, как обработать адрес ${request.url}`);
    }
    
    // Создаётся сам сервер, в качестве параметра передаётся функция, которая обслуживает запросы
    const server = http.createServer(requestProcessor);
    
    // Созданный сервер запускается
    server.listen(8888, function() {
        console.log('Сервер запущен на 8888-м порту. Откройте http://localhost:8888/ для проверки');
    });


    В одном JS-файле можно запустить даже больше одного сервера — это всё управляется кодом на JS, а сам NodeJS только помогает этот код запустить.
    Ответ написан
    2 комментария
  • Ошибка при подключении к MongoDB. Как исправить?

    nowm
    @nowm
    В драйвере MongoDB для Java нет класса MongoClients (с буквой s в конце). Есть класс MongoClient.

    Так что, нужно писать так:

    MongoClient mongoClient = MongoClient.create("mongodb link");


    И если вы где-то делаете import com.mongodb.client.MongoClients — удалите, это не правильно.

    Edit: в некоторых версиях драйвера у класса MongoClient нет статического метода create. В этом случае клиент создаётся так:

    MongoClient mongoClient = new MongoClient("mongodb link");
    Ответ написан
    2 комментария
  • Как перманентно поставить владельца файла .sock на mongodb?

    nowm
    @nowm
    Посмотрите конфигурацию сервиса mongod. Она должна быть в файле /lib/systemd/system/mongod.service.

    После названия раздела [Service] должны идти эти строки:
    User=mongodb
    Group=mongodb


    Это влияет на то, кому будет принадлежать файл /tmp/mongodb-27017.sock. Так же, запускать/перезапускать сервис лучше только командой sudo systemctl start mongod.service

    Ещё, судя по тому, что у вас в команде systemctl написано mongod, а не mongod.service, вы, возможно используете собственную конфигурацию для запуска, и тогда нужно редактировать другой файл — вы должны быть в курсе, какой именно, потому что наверняка сами его писали. В оригинальном файле с конфигурацией сервиса, который по-умолчанию создаётся после установки MongoDB, целевой пользователь/группа прописываются правильно и проблем не создают.
    Ответ написан
    5 комментариев
  • Как правильно раздвинуть кнопки?

    nowm
    @nowm
    Кнопки растягиваются корректно, но вы используете бутстрап и класс .container, и бутстрап ограничивает ширину этого элемента, потому что это служебный для бутстрапа класс и он для него прописывает много собственных стилей. Блоки с кнопками, соответственно, за пределы родительского контейнера не вылезают.

    Как один из фиксов, класс container можно заменить на d-flex justify-content-between и они начнут нормально растягиваться.
    Ответ написан
    Комментировать
  • Почему на некоторых сайтах страницы не открываются в новом окне при ctrl+click?

    nowm
    @nowm
    Это происходит из-за того, что ссылка находится внутри элемента, на который навешано прослушивание события click. При этом это навешанное событие не проверяет ни источник клика, ни использование модификаторов (shift, alt, ctrl и так далее). Далее, когда вы кликаете по ссылке, при условии, что навигация происходит не в текущей вкладке, событие клика передаётся по цепочке всем родителям этой ссылки, и если один из родителей слушает click, он его обработает.

    Это обычное следствие кривых рук отсутствия опыта — никакой стратегии тут нет. Люди просто не умеют просчитывать варианты того, в каких обстоятельствах будет работать их код.

    И если перейти на частности, то на сайте, который вы упомянули, на строку таблицы, внутри которой находится ссылка, навешано событие, которое при клике открывает страницу /en/subtitles/***. Если вы просто кликаете по ссылке, то вы переходите по корректному адресу, потому что используется та же самая вкладка, но если вы открываете ссылку в новом окне, кликая не правой клавишей мыши, а левой плюс кнопка-модификатор Ctrl, событие клика передаётся в тег TR, который переадресовывает текущую вкладку на адрес /en/subtitles/***.
    Ответ написан
    2 комментария
  • Phpstorm deployment подключение к проду через другой сервер?

    nowm
    @nowm
    Чтобы поправить терминологию: в PhpStorm есть Delpoyment — выгрузка файлов на целевой сервер, а есть Code With Me — редактирование кода онлайн вместе с другими пользователями. Так что фраза «редактирования кода online (deployment)» — это неправильно, потому что это две разные вещи.

    Обе эти вещи сделать нельзя, используя только PhpStorm. Например, для деплоя можно использовать SFTP, но конфигурации SSH при этом не поддерживают проксирование одного сервера другим (нужно делать SSH-тоннель за пределами PhpStorm). Code With Me тоже такого не позволяет делать, потому что там сервер — это хост, на котором должен быть запущен PhpStorm, и нужно делать маппинг портов за пределами PhpStorm, чтобы организовать такую схему.
    Ответ написан
    1 комментарий
  • Как быстро и надежно закрывать задачи по сайту не нанимая программиста?

    nowm
    @nowm
    Могу ответить с точки зрения фрилансера:

    Если через пару месяцев сложно рассчитывать на работу, значит у вас есть какие-то проблемы в организации работы. Например, с вами психологически тяжело общаться в процессе или вы не спешите оплачивать работу, задерживаете оплату и т.п. Если человеку стало некомфортно с вами работать, он обычно доделывает текущую работу, но дальше с вами работать никогда не будет — будет только придумывать отмазки, что сейчас завален работой или тому подобное. Напрямую такие вещи высказываются очень редко.

    Вот несколько советов, которые помогут улучшить «возвращаемость» фрилансеров:
    • Чаще всего фрилансер готов делать работу сразу (особенно когда работает с вами первый раз), он показывает рвение и очень быстро всё делает. С вашей стороны ожидается, что вы можете оплатить работу мгновенно после того, как всё сделано и проверено как фрилансером так и вами. Если вы скажете фрилансеру, что вам нужно некоторое время, чтобы дойти до банкомата и пополнить счёт, потому что прямо сейчас не можете заплатить, это очень сильно испортит впечатление, и с большой вероятностью человек не покажет виду, спокойно без психов подождёт, когда вы ему заплатите, но в следующий раз работать с вами больше не будет. Фрилансер готов работать сразу, значит и вы должны быть готовы платить сразу.
    • Если у вас низкая техническая подготовка, и фрилансеру приходится кроме самого кода очень много общаться с вами на тему, что именно нужно делать, желание работать дальше тоже пропадает. Чем меньше требуется обсуждений и созвонов в мессенджере, тем больше веротность, что фрилансер и дальше будет с вами работать. Фрилансеры обычно не считают переписку частью оплачиваемой работы и, соответственно, чем больше времени будет занимать переписка, тем меньше будет желание продолжать работу. Фрилансер обычно хочет сразу делать работу и не отвлекаться на всякую бесплатную болтологию.
    • Если фрилансеру нужно заниматься в дополнение администрированием вашего сервера, а это заранее не обговаривалось, это тоже может испортить впечатление. Часто заказчики пишут, что им нужно поправить менюшку на сайте, а это в итоге превращается в правку менюшки, а потом залитие кода на сайт, настройка и перезапуск сервера и ещё какие-то подобные работы, которых в задании не было вообще. И, скорее всего, вы считаете само собой разумеющимся, что фрилансер должен уметь это делать, и думаете, что это нормальная практика, если он в дополнение к кодингу будет делать ещё и это. Дополнительно, если эти моменты ещё и не оплачиваются, шансы, что люди захотят с вами работать и дальше, снижаются ещё сильнее.
    • Некоторые заказчики любят показывать свой авторитет. Запомните, что для фрилансера вы не начальник, а равноправный партнёр, и он ждёт от вас соответствующего уровня общения. Если вы строите из себя начальника, люди будут придумывать любые отмазки, но повторно работать не станут.


    Вот это основные факторы, которые влияют на то, что вам ответит фрилансер, если вам понадобится помощь в следующий раз, и вы решите, что можно обратиться к тому, с кем раньше работали.
    Ответ написан
    8 комментариев