/** * Chatwoot Work Schedule V14 * Dashboard Script + n8n Data Tables * * Regras principais: * - Account config: account_id * - Profile: account_id + profile_id * - User: account_id + user_id * - O id interno da Data Table do n8n nunca e usado pelo navegador. * * Endpoints n8n esperados: * GET /work-schedule/accounts/list * GET /work-schedule/accounts/status?account_id=... * POST /work-schedule/accounts/toggle * GET /work-schedule/profiles/list?account_id=... * POST /work-schedule/profiles/save * POST /work-schedule/profiles/delete * GET /work-schedule/check?account_id=...&user_id=... */ (function () { 'use strict'; const VERSION = '14.1.0'; const INSTANCE_KEY = '__CW_WORK_SCHEDULE_V14__'; if (window[INSTANCE_KEY]?.destroy) { try { window[INSTANCE_KEY].destroy(); } catch (_) {} } const CONFIG = { debug: true, n8nBaseURL: 'https://webhookn8n.agencia4am-automacoes.com.br/webhook', masterAccountIds: ['54', '57', '1'], checkIntervalMs: 60_000, contextPollMs: 1_500, defaultTimezone: 'America/Sao_Paulo', defaultWarningMinutes: 15, moduleLabel: 'Horários de trabalho', }; const DAYS = [ { key: 'seg', short: 'Seg', label: 'Segunda-feira' }, { key: 'ter', short: 'Ter', label: 'Terça-feira' }, { key: 'qua', short: 'Qua', label: 'Quarta-feira' }, { key: 'qui', short: 'Qui', label: 'Quinta-feira' }, { key: 'sex', short: 'Sex', label: 'Sexta-feira' }, { key: 'sab', short: 'Sáb', label: 'Sábado' }, { key: 'dom', short: 'Dom', label: 'Domingo' }, ]; const STATE = { destroyed: false, currentUser: null, accountId: null, accountRole: 'agent', isAdminOfAccount: false, isMaster: false, feature: { loaded: false, enabled: false, timezone: CONFIG.defaultTimezone, warningMinutes: CONFIG.defaultWarningMinutes, }, pageOpen: false, pageMode: null, pageRoot: null, menuItem: null, menuObserver: null, lockObserver: null, checkTimer: null, contextTimer: null, currentAccess: null, profiles: [], agents: [], companies: [], editingProfileId: null, pendingNewProfile: null, lastAccountSeen: null, listeners: [], }; function log(...args) { if (!CONFIG.debug) return; console.log(`[WorkSchedule V${VERSION}]`, ...args); } function logError(...args) { console.error(`[WorkSchedule V${VERSION}]`, ...args); } function addListener(target, event, handler, options) { target.addEventListener(event, handler, options); STATE.listeners.push(() => target.removeEventListener(event, handler, options) ); } function escapeHtml(value) { return String(value ?? '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function escapeAttr(value) { return escapeHtml(value).replace(/`/g, '`'); } function safeJsonParse(value, fallback) { if ( value === null || value === undefined || value === '' ) { return fallback; } if (typeof value === 'object') { return value; } try { return JSON.parse(value); } catch (_) { return fallback; } } function uniqueStrings(values) { return [ ...new Set( (Array.isArray(values) ? values : []) .map(v => String(v)) .filter(Boolean) ), ]; } function makeProfileId() { if (window.crypto?.randomUUID) { return `prof_${window.crypto.randomUUID()}`; } return ( `prof_${Date.now()}_` + Math.random() .toString(36) .slice(2, 10) ); } function getAccountIdFromLocation() { const match = window.location.pathname.match( /\/accounts\/(\d+)/ ); if (match) { return String(match[1]); } const switcher = document.getElementById( 'sidebar-account-switcher' ); if ( switcher?.dataset?.accountId ) { return String( switcher.dataset.accountId ); } if ( window.chatwootConfig ?.currentAccountId ) { return String( window.chatwootConfig .currentAccountId ); } return null; } function getAuthHeaders() { const headers = {}; try { const common = window.axios?.defaults ?.headers?.common || {}; [ 'access-token', 'token-type', 'client', 'expiry', 'uid', ].forEach(key => { if ( common[key] !== undefined && common[key] !== null && common[key] !== '' ) { headers[key] = common[key]; } }); } catch (_) {} return headers; } async function chatwootRequest( path, options = {} ) { const headers = { Accept: 'application/json', ...getAuthHeaders(), ...(options.headers || {}), }; if ( options.body && !headers['Content-Type'] ) { headers['Content-Type'] = 'application/json'; } const response = await fetch(path, { credentials: 'same-origin', ...options, headers, }); let data = null; const text = await response.text(); if (text) { try { data = JSON.parse(text); } catch (_) { data = text; } } if (!response.ok) { const err = new Error( `Chatwoot HTTP ${response.status}` ); err.status = response.status; err.data = data; throw err; } return data; } async function n8nRequest( path, options = {} ) { const url = `${CONFIG.n8nBaseURL}${path}`; const headers = { Accept: 'application/json', ...(options.headers || {}), }; if ( options.body && !headers['Content-Type'] ) { headers['Content-Type'] = 'application/json'; } const response = await fetch(url, { cache: 'no-store', ...options, headers, }); const text = await response.text(); let data = null; if (text) { try { data = JSON.parse(text); } catch (_) { data = { raw: text, }; } } if (!response.ok) { const err = new Error( `N8N HTTP ${response.status}` ); err.status = response.status; err.data = data; throw err; } return data || {}; } function normalizeRole( profileData, accountId ) { const account = Array.isArray( profileData?.accounts ) ? profileData.accounts.find( a => String(a.id) === String(accountId) ) : null; const role = account?.role || account?.user_role || profileData?.role || window.chatwootConfig ?.currentUserRole || 'agent'; return String( role || 'agent' ).toLowerCase(); } async function loadContext( force = false ) { const accountId = getAccountIdFromLocation(); if (!accountId) { return false; } const accountChanged = String(accountId) !== String( STATE.accountId || '' ); if ( !force && STATE.currentUser && !accountChanged ) { return true; } let profileData = null; try { profileData = await chatwootRequest( '/api/v1/profile' ); } catch (error) { logError( 'Falha ao carregar /api/v1/profile', error ); } if (profileData?.id) { STATE.currentUser = profileData; } else if ( window.chatwootConfig ?.currentUserId ) { STATE.currentUser = { id: window.chatwootConfig .currentUserId, name: window.chatwootConfig .currentUserName || 'Usuário', role: window.chatwootConfig .currentUserRole || 'agent', accounts: [], }; } if (!STATE.currentUser?.id) { return false; } STATE.accountId = String(accountId); STATE.accountRole = normalizeRole( profileData || STATE.currentUser, STATE.accountId ); STATE.isAdminOfAccount = STATE.accountRole === 'administrator' || STATE.accountRole === 'admin'; STATE.isMaster = CONFIG.masterAccountIds.includes( STATE.accountId ) && STATE.isAdminOfAccount; STATE.lastAccountSeen = STATE.accountId; await refreshFeatureStatus(); return true; } async function refreshFeatureStatus() { if (!STATE.accountId) { return false; } if (STATE.isMaster) { STATE.feature = { loaded: true, enabled: true, timezone: CONFIG.defaultTimezone, warningMinutes: CONFIG.defaultWarningMinutes, }; refreshMenuVisibility(); return true; } try { const data = await n8nRequest( `/work-schedule/accounts/status?account_id=${encodeURIComponent( STATE.accountId )}&_t=${Date.now()}` ); STATE.feature = { loaded: true, enabled: data.enabled === true && data.feature_disabled !== true, timezone: data.timezone || CONFIG.defaultTimezone, warningMinutes: Number.isFinite( Number( data.warning_minutes ) ) ? Number( data.warning_minutes ) : CONFIG.defaultWarningMinutes, }; } catch (error) { logError( 'Falha ao consultar status do módulo. Fail-open.', error ); STATE.feature = { ...STATE.feature, loaded: true, enabled: false, }; } refreshMenuVisibility(); return STATE.feature.enabled; } function canSeeModuleMenu() { if (STATE.isMaster) { return true; } return ( STATE.isAdminOfAccount && STATE.feature.loaded && STATE.feature.enabled ); } function injectStyles() { if ( document.getElementById( 'cw-wt-v14-styles' ) ) { return; } const style = document.createElement( 'style' ); style.id = 'cw-wt-v14-styles'; style.textContent = ` #cw-wt-page, #cw-wt-lock, #cw-wt-warning, #cw-wt-toast-root { --wt-bg: var( --color-n-background, var( --color-n-surface-1, #ffffff ) ); --wt-surface: var( --color-n-solid-2, #ffffff ); --wt-surface-soft: var( --color-n-solid-1, #f8fafc ); --wt-surface-hover: var( --color-n-alpha-2, rgba( 15, 23, 42, .05 ) ); --wt-border: var( --color-n-weak, #e2e8f0 ); --wt-border-strong: var( --color-n-slate-5, #cbd5e1 ); --wt-text: var( --color-n-slate-12, #0f172a ); --wt-text-muted: var( --color-n-slate-11, #64748b ); --wt-text-subtle: var( --color-n-slate-10, #94a3b8 ); --wt-brand: var( --color-n-brand, #1f93ff ); --wt-brand-soft: var( --color-n-brand-2, rgba( 31, 147, 255, .10 ) ); --wt-danger: var( --color-n-ruby-11, #dc2626 ); --wt-danger-soft: var( --color-n-ruby-2, rgba( 220, 38, 38, .08 ) ); --wt-success: var( --color-n-teal-11, #0f766e ); --wt-success-soft: var( --color-n-teal-3, rgba( 13, 148, 136, .10 ) ); --wt-warning: var( --color-n-amber-11, #b45309 ); --wt-warning-soft: var( --color-n-amber-3, rgba( 245, 158, 11, .12 ) ); color: var(--wt-text); font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; } .dark #cw-wt-page, .dark #cw-wt-lock, .dark #cw-wt-warning, .dark #cw-wt-toast-root { --wt-bg: var( --color-n-background, #0b0f19 ); --wt-surface: var( --color-n-solid-2, #151b27 ); --wt-surface-soft: var( --color-n-solid-1, #101621 ); --wt-surface-hover: var( --color-n-alpha-2, rgba( 255, 255, 255, .05 ) ); --wt-border: var( --color-n-weak, #273142 ); --wt-border-strong: var( --color-n-slate-5, #39475d ); --wt-text: var( --color-n-slate-12, #f8fafc ); --wt-text-muted: var( --color-n-slate-11, #cbd5e1 ); --wt-text-subtle: var( --color-n-slate-10, #94a3b8 ); } #cw-wt-page { position: fixed; z-index: 1200; background: var(--wt-bg); overflow: auto; box-sizing: border-box; border-left: 1px solid var(--wt-border); } #cw-wt-page * { box-sizing: border-box; } #cw-wt-page button, #cw-wt-page input, #cw-wt-page select { font: inherit; } .cw-wt-shell { width: min( 1180px, calc( 100% - 48px ) ); margin: 0 auto; padding: 28px 0 64px; } .cw-wt-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; padding-bottom: 20px; border-bottom: 1px solid var(--wt-border); } .cw-wt-title-wrap { min-width: 0; } .cw-wt-eyebrow { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; color: var( --wt-text-muted ); font-size: 12px; font-weight: 600; } .cw-wt-title { margin: 0; color: var(--wt-text); font-size: 24px; line-height: 1.25; font-weight: 700; letter-spacing: -.02em; } .cw-wt-subtitle { margin: 6px 0 0; max-width: 760px; color: var( --wt-text-muted ); font-size: 14px; line-height: 1.55; } .cw-wt-header-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; } .cw-wt-btn { height: 38px; border-radius: 8px; border: 1px solid var(--wt-border); background: var(--wt-surface); color: var(--wt-text); padding: 0 14px; display: inline-flex; align-items: center; justify-content: center; gap: 7px; cursor: pointer; font-size: 13px; font-weight: 600; transition: background .15s, border-color .15s, opacity .15s; } .cw-wt-btn:hover:not(:disabled) { background: var( --wt-surface-hover ); border-color: var( --wt-border-strong ); } .cw-wt-btn:disabled { opacity: .55; cursor: not-allowed; } .cw-wt-btn-primary { background: var(--wt-brand); border-color: var(--wt-brand); color: #fff; } .cw-wt-btn-primary:hover:not(:disabled) { filter: brightness(1.04); background: var(--wt-brand); border-color: var(--wt-brand); } .cw-wt-btn-danger { color: var(--wt-danger); background: transparent; border-color: transparent; } .cw-wt-btn-danger:hover:not(:disabled) { background: var( --wt-danger-soft ); border-color: transparent; } .cw-wt-btn-link { height: auto; padding: 3px 2px; border: 0; background: transparent; color: var(--wt-brand); } .cw-wt-btn-link:hover:not(:disabled) { background: transparent; text-decoration: underline; border: 0; } .cw-wt-icon-btn { width: 34px; height: 34px; padding: 0; border-radius: 8px; } .cw-wt-content { padding-top: 22px; } .cw-wt-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; } .cw-wt-search { position: relative; width: min( 360px, 100% ); } .cw-wt-input, .cw-wt-select { width: 100%; height: 38px; border: 1px solid var(--wt-border); border-radius: 8px; background: var(--wt-surface); color: var(--wt-text); padding: 0 11px; outline: none; font-size: 13px; } .cw-wt-input:focus, .cw-wt-select:focus { border-color: var(--wt-brand); box-shadow: 0 0 0 3px var(--wt-brand-soft); } .cw-wt-input::placeholder { color: var( --wt-text-subtle ); } .cw-wt-field { display: flex; flex-direction: column; gap: 6px; } .cw-wt-label { color: var( --wt-text-muted ); font-size: 12px; font-weight: 600; } .cw-wt-help { color: var( --wt-text-subtle ); font-size: 12px; line-height: 1.45; } .cw-wt-card { border: 1px solid var(--wt-border); border-radius: 10px; background: var(--wt-surface); } .cw-wt-card + .cw-wt-card { margin-top: 12px; } .cw-wt-profile-card { padding: 16px 18px; display: flex; align-items: center; gap: 16px; justify-content: space-between; } .cw-wt-profile-main { min-width: 0; flex: 1; } .cw-wt-profile-name-row { display: flex; align-items: center; gap: 8px; min-width: 0; } .cw-wt-profile-name { color: var(--wt-text); font-size: 14px; font-weight: 650; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .cw-wt-profile-meta { margin-top: 5px; color: var( --wt-text-muted ); font-size: 12px; line-height: 1.5; } .cw-wt-profile-actions { flex-shrink: 0; display: flex; align-items: center; gap: 8px; } .cw-wt-badge { display: inline-flex; align-items: center; height: 22px; padding: 0 8px; border-radius: 999px; font-size: 11px; font-weight: 650; white-space: nowrap; } .cw-wt-badge-success { color: var(--wt-success); background: var( --wt-success-soft ); } .cw-wt-badge-muted { color: var( --wt-text-muted ); background: var( --wt-surface-soft ); } .cw-wt-badge-danger { color: var(--wt-danger); background: var( --wt-danger-soft ); } .cw-wt-empty { border: 1px dashed var( --wt-border-strong ); border-radius: 10px; padding: 50px 22px; text-align: center; background: var( --wt-surface-soft ); } .cw-wt-empty-title { margin: 0 0 7px; color: var(--wt-text); font-size: 15px; font-weight: 650; } .cw-wt-empty-text { margin: 0 auto 16px; max-width: 520px; color: var( --wt-text-muted ); font-size: 13px; line-height: 1.55; } .cw-wt-loading { padding: 48px 16px; text-align: center; color: var( --wt-text-muted ); font-size: 13px; } .cw-wt-error { padding: 14px 16px; border: 1px solid color-mix( in srgb, var(--wt-danger) 28%, var(--wt-border) ); background: var( --wt-danger-soft ); color: var(--wt-danger); border-radius: 9px; font-size: 13px; line-height: 1.5; } .cw-wt-editor { display: grid; grid-template-columns: minmax(0,1fr); gap: 18px; } .cw-wt-section { border: 1px solid var(--wt-border); border-radius: 10px; background: var(--wt-surface); overflow: hidden; } .cw-wt-section-head { padding: 15px 17px; border-bottom: 1px solid var(--wt-border); display: flex; align-items: center; justify-content: space-between; gap: 12px; } .cw-wt-section-title { color: var(--wt-text); font-size: 13px; font-weight: 650; } .cw-wt-section-desc { margin-top: 3px; color: var( --wt-text-muted ); font-size: 12px; line-height: 1.45; } .cw-wt-section-body { padding: 17px; } .cw-wt-days { display: flex; flex-direction: column; } .cw-wt-day { padding: 14px 0; border-bottom: 1px solid var(--wt-border); display: grid; grid-template-columns: 180px minmax(0,1fr); gap: 18px; align-items: flex-start; } .cw-wt-day:last-child { border-bottom: 0; } .cw-wt-day-label-wrap { display: flex; align-items: center; gap: 10px; min-height: 34px; } .cw-wt-day-label { color: var(--wt-text); font-size: 13px; font-weight: 600; } .cw-wt-day.closed .cw-wt-periods { opacity: .55; pointer-events: none; } .cw-wt-day.closed .cw-wt-add-period { display: none; } .cw-wt-periods { display: flex; flex-direction: column; gap: 8px; } .cw-wt-period-row { display: flex; align-items: center; gap: 8px; } .cw-wt-time { width: 112px; } .cw-wt-period-sep { color: var( --wt-text-subtle ); font-size: 12px; } .cw-wt-no-periods { color: var( --wt-text-subtle ); font-size: 12px; line-height: 34px; } .cw-wt-switch { position: relative; width: 34px; height: 20px; display: inline-flex; flex-shrink: 0; } .cw-wt-switch input { opacity: 0; width: 0; height: 0; position: absolute; } .cw-wt-switch-track { position: absolute; inset: 0; border-radius: 999px; background: var( --wt-border-strong ); transition: .15s; cursor: pointer; } .cw-wt-switch-track:before { content: ""; position: absolute; width: 16px; height: 16px; left: 2px; top: 2px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba( 0, 0, 0, .18 ); transition: .15s; } .cw-wt-switch input:checked + .cw-wt-switch-track { background: var(--wt-brand); } .cw-wt-switch input:checked + .cw-wt-switch-track:before { transform: translateX(14px); } .cw-wt-agent-toolbar { display: flex; align-items: center; gap: 12px; justify-content: space-between; margin-bottom: 12px; flex-wrap: wrap; } .cw-wt-agent-actions { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; } .cw-wt-agent-list { border: 1px solid var(--wt-border); border-radius: 9px; overflow: auto; max-height: 320px; } .cw-wt-agent-row { min-height: 48px; padding: 9px 12px; display: flex; align-items: center; justify-content: space-between; gap: 12px; border-bottom: 1px solid var(--wt-border); } .cw-wt-agent-row:last-child { border-bottom: 0; } .cw-wt-agent-row.is-disabled { opacity: .55; background: var( --wt-surface-soft ); } .cw-wt-agent-left { display: flex; align-items: center; gap: 10px; min-width: 0; } .cw-wt-agent-check { width: 16px; height: 16px; accent-color: var(--wt-brand); flex-shrink: 0; } .cw-wt-agent-info { min-width: 0; } .cw-wt-agent-name { color: var(--wt-text); font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .cw-wt-agent-sub { margin-top: 2px; color: var( --wt-text-muted ); font-size: 11px; } .cw-wt-agent-conflict { color: var(--wt-danger); font-size: 11px; font-weight: 600; text-align: right; } .cw-wt-editor-footer { position: sticky; bottom: 0; margin-top: 4px; padding: 12px 0 0; display: flex; justify-content: flex-end; gap: 8px; background: linear-gradient( to bottom, transparent, var(--wt-bg) 28% ); } .cw-wt-table-wrap { border: 1px solid var(--wt-border); border-radius: 10px; overflow: auto; background: var(--wt-surface); } .cw-wt-table { width: 100%; border-collapse: collapse; min-width: 720px; } .cw-wt-table th { text-align: left; padding: 11px 14px; background: var( --wt-surface-soft ); color: var( --wt-text-muted ); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; font-weight: 650; border-bottom: 1px solid var(--wt-border); } .cw-wt-table td { padding: 13px 14px; color: var(--wt-text); font-size: 13px; border-bottom: 1px solid var(--wt-border); vertical-align: middle; } .cw-wt-table tr:last-child td { border-bottom: 0; } .cw-wt-table tbody tr:hover { background: var( --wt-surface-hover ); } .cw-wt-table .right { text-align: right; } .cw-wt-company-sub { margin-top: 2px; color: var( --wt-text-muted ); font-size: 11px; } #cw-wt-warning { position: fixed; top: 14px; left: 50%; transform: translateX(-50%); z-index: 2147483000; width: min( 680px, calc( 100vw - 28px ) ); pointer-events: none; } .cw-wt-warning-card { pointer-events: auto; border: 1px solid color-mix( in srgb, var(--wt-warning) 28%, var(--wt-border) ); background: var(--wt-surface); border-radius: 10px; padding: 11px 13px; display: flex; align-items: flex-start; gap: 10px; box-shadow: 0 12px 30px rgba( 0, 0, 0, .14 ); } .cw-wt-warning-icon { width: 28px; height: 28px; border-radius: 8px; display: grid; place-items: center; background: var( --wt-warning-soft ); color: var(--wt-warning); flex-shrink: 0; } .cw-wt-warning-title { color: var(--wt-text); font-size: 13px; font-weight: 650; } .cw-wt-warning-text { margin-top: 2px; color: var( --wt-text-muted ); font-size: 12px; line-height: 1.45; } #cw-wt-lock { position: fixed !important; inset: 0 !important; z-index: 2147483647 !important; background: var(--wt-bg) !important; display: flex !important; align-items: center !important; justify-content: center !important; padding: 24px !important; overflow: auto !important; } .cw-wt-lock-card { width: min( 440px, 100% ); text-align: center; } .cw-wt-lock-icon { width: 48px; height: 48px; margin: 0 auto 16px; display: grid; place-items: center; border-radius: 12px; background: var( --wt-surface-soft ); color: var( --wt-text-muted ); border: 1px solid var(--wt-border); } .cw-wt-lock-title { margin: 0; color: var(--wt-text); font-size: 21px; font-weight: 700; letter-spacing: -.02em; } .cw-wt-lock-text { margin: 8px auto 0; color: var( --wt-text-muted ); font-size: 13px; line-height: 1.55; max-width: 390px; } .cw-wt-lock-next { margin: 20px 0; border: 1px solid var(--wt-border); background: var(--wt-surface); border-radius: 10px; padding: 13px 15px; text-align: left; } .cw-wt-lock-next-label { color: var( --wt-text-subtle ); font-size: 11px; font-weight: 650; text-transform: uppercase; letter-spacing: .04em; } .cw-wt-lock-next-value { margin-top: 5px; color: var(--wt-text); font-size: 13px; font-weight: 600; } .cw-wt-lock-profile { margin-top: 4px; color: var( --wt-text-muted ); font-size: 12px; } .cw-wt-lock-actions { margin-top: 18px; } .cw-wt-lock-actions .cw-wt-btn { width: 100%; height: 40px; } #cw-wt-toast-root { position: fixed; right: 18px; bottom: 18px; z-index: 2147483500; width: min( 380px, calc( 100vw - 36px ) ); display: flex; flex-direction: column; gap: 8px; pointer-events: none; } .cw-wt-toast { pointer-events: auto; border: 1px solid var(--wt-border); background: var(--wt-surface); color: var(--wt-text); border-radius: 9px; padding: 11px 13px; box-shadow: 0 12px 28px rgba( 0, 0, 0, .14 ); font-size: 12px; line-height: 1.45; } .cw-wt-toast.error { border-color: color-mix( in srgb, var(--wt-danger) 35%, var(--wt-border) ); } .cw-wt-toast.success { border-color: color-mix( in srgb, var(--wt-success) 35%, var(--wt-border) ); } /* ========================================== * V14.1 - DESIGN CHATWOOT 4.16.2 * ========================================== */ #cw-wt-page .cw-wt-shell { width: min( 1380px, calc( 100% - 32px ) ); padding: 20px 0 48px; } #cw-wt-page .cw-wt-content { padding-top: 16px; } #cw-wt-page .cw-wt-header { gap: 16px; padding-bottom: 16px; } #cw-wt-page .cw-wt-eyebrow { margin-bottom: 4px; font-size: 11px; line-height: 16px; font-weight: 500; } #cw-wt-page .cw-wt-title { font-size: 20px; line-height: 28px; font-weight: 600; letter-spacing: -.01em; } #cw-wt-page .cw-wt-subtitle { margin-top: 4px; font-size: 13px; line-height: 19px; max-width: 800px; } #cw-wt-page .cw-wt-btn, #cw-wt-lock .cw-wt-btn { min-height: 32px; height: 32px; padding: 0 12px; gap: 7px; border-radius: 8px; font-size: 13px; line-height: 18px; font-weight: 500; box-shadow: none; border-width: 1px; transition: background-color .1s ease, border-color .1s ease, transform .1s ease; } #cw-wt-page .cw-wt-btn:active:not(:disabled), #cw-wt-lock .cw-wt-btn:active:not(:disabled) { transform: scale(.98); } #cw-wt-page .cw-wt-btn-primary { border-color: transparent; } #cw-wt-page .cw-wt-btn-link { min-height: 28px; height: 28px; padding: 0 9px; border: 0; border-radius: 7px; text-decoration: none; color: var( --wt-text-muted ); background: transparent; font-size: 12px; font-weight: 500; } #cw-wt-page .cw-wt-btn-link:hover:not(:disabled) { text-decoration: none; background: var( --wt-surface-hover ); color: var(--wt-text); border: 0; } #cw-wt-page .cw-wt-card, #cw-wt-page .cw-wt-section, #cw-wt-page .cw-wt-table-wrap { border-radius: 10px; border: 1px solid var(--wt-border); box-shadow: 0 1px 2px rgba( 0, 0, 0, .035 ); } #cw-wt-page .cw-wt-section-head { min-height: 56px; padding: 11px 14px; } #cw-wt-page .cw-wt-section-body { padding: 14px; } #cw-wt-page .cw-wt-section-title { font-size: 13px; line-height: 18px; font-weight: 600; } #cw-wt-page .cw-wt-section-desc { margin-top: 2px; font-size: 11.5px; line-height: 17px; } /* * Desktop: * * [ Perfil ] * [ Horário ][ Usuários ] * [ footer ] */ #cw-wt-page .cw-wt-editor { display: grid; grid-template-columns: minmax( 520px, 1.55fr ) minmax( 340px, .85fr ); gap: 14px; align-items: start; } #cw-wt-page .cw-wt-editor > .cw-wt-section:nth-of-type(1) { grid-column: 1 / -1; } #cw-wt-page .cw-wt-editor > .cw-wt-section:nth-of-type(2) { grid-column: 1; } #cw-wt-page .cw-wt-editor > .cw-wt-section:nth-of-type(3) { grid-column: 2; position: sticky; top: 12px; } #cw-wt-page .cw-wt-editor-footer { grid-column: 1 / -1; } #cw-wt-page .cw-wt-input, #cw-wt-page .cw-wt-select { height: 34px; min-height: 34px; padding: 0 10px; border-radius: 8px; font-size: 13px; font-weight: 400; } #cw-wt-page .cw-wt-label { font-size: 11.5px; line-height: 16px; font-weight: 500; } #cw-wt-page .cw-wt-day { padding: 10px 0; grid-template-columns: 126px minmax( 0, 1fr ); gap: 10px; } #cw-wt-page .cw-wt-day-label-wrap { gap: 8px; min-height: 32px; } #cw-wt-page .cw-wt-day-label { font-size: 12.5px; line-height: 18px; font-weight: 500; } #cw-wt-page .cw-wt-periods { gap: 5px; } #cw-wt-page .cw-wt-period-list { display: flex; flex-direction: column; gap: 5px; } #cw-wt-page .cw-wt-period-row { display: grid; grid-template-columns: 96px 18px 96px 32px; align-items: center; justify-content: start; gap: 5px; width: max-content; max-width: 100%; } #cw-wt-page .cw-wt-time { width: 96px; min-width: 0; height: 32px; min-height: 32px; padding: 0 6px; font-size: 12px; text-align: center; } #cw-wt-page .cw-wt-period-sep { width: 18px; text-align: center; font-size: 11px; } #cw-wt-page .cw-wt-icon-btn { width: 32px; min-width: 32px; height: 32px; min-height: 32px; padding: 0; } #cw-wt-page .cw-wt-remove-period { color: var( --wt-text-subtle ); border-color: transparent; background: transparent; } #cw-wt-page .cw-wt-remove-period:hover { color: var(--wt-danger); background: var( --wt-danger-soft ); } #cw-wt-page .cw-wt-add-period { margin-top: 1px; } #cw-wt-page .cw-wt-switch { width: 32px; height: 18px; } #cw-wt-page .cw-wt-switch-track:before { width: 14px; height: 14px; top: 2px; left: 2px; } #cw-wt-page .cw-wt-switch input:checked + .cw-wt-switch-track:before { transform: translateX(14px); } #cw-wt-page .cw-wt-agent-toolbar { margin-bottom: 9px; gap: 8px; } #cw-wt-page .cw-wt-agent-toolbar .cw-wt-search { width: 100%; } #cw-wt-page .cw-wt-agent-actions { width: 100%; display: flex; align-items: center; justify-content: flex-start; gap: 3px; flex-wrap: wrap; } #cw-wt-page .cw-wt-agent-list { max-height: calc( 100vh - 370px ); min-height: 260px; border-radius: 8px; } #cw-wt-page .cw-wt-agent-row { min-height: 44px; padding: 7px 9px; gap: 8px; } #cw-wt-page .cw-wt-agent-left { gap: 8px; } #cw-wt-page .cw-wt-agent-check { width: 15px; height: 15px; } #cw-wt-page .cw-wt-agent-name { font-size: 12.5px; line-height: 17px; font-weight: 550; } #cw-wt-page .cw-wt-agent-sub { margin-top: 1px; font-size: 10.5px; line-height: 15px; } #cw-wt-page .cw-wt-agent-conflict { max-width: 120px; font-size: 10px; line-height: 14px; } #cw-wt-page .cw-wt-profile-card { min-height: 58px; padding: 11px 13px; gap: 10px; } #cw-wt-page .cw-wt-profile-name { font-size: 13px; line-height: 18px; font-weight: 600; } #cw-wt-page .cw-wt-profile-meta { margin-top: 3px; font-size: 11.5px; line-height: 17px; } #cw-wt-page .cw-wt-badge { height: 20px; padding: 0 7px; font-size: 10px; line-height: 14px; font-weight: 600; } #cw-wt-page .cw-wt-editor-footer { padding: 10px 0 2px; gap: 6px; } #cw-wt-page .cw-wt-table th { padding: 9px 12px; font-size: 10.5px; font-weight: 600; } #cw-wt-page .cw-wt-table td { padding: 10px 12px; font-size: 12.5px; } #cw-wt-page .cw-wt-company-sub { font-size: 10.5px; } @media ( max-width: 1100px ) { #cw-wt-page .cw-wt-editor { grid-template-columns: 1fr; } #cw-wt-page .cw-wt-editor > .cw-wt-section:nth-of-type(1), #cw-wt-page .cw-wt-editor > .cw-wt-section:nth-of-type(2), #cw-wt-page .cw-wt-editor > .cw-wt-section:nth-of-type(3), #cw-wt-page .cw-wt-editor-footer { grid-column: 1; } #cw-wt-page .cw-wt-editor > .cw-wt-section:nth-of-type(3) { position: static; } #cw-wt-page .cw-wt-agent-list { max-height: 340px; } } @media ( max-width: 700px ) { #cw-wt-page .cw-wt-shell { width: calc( 100% - 20px ); } #cw-wt-page .cw-wt-day { grid-template-columns: 1fr; } #cw-wt-page .cw-wt-period-row { grid-template-columns: minmax( 80px, 1fr ) 16px minmax( 80px, 1fr ) 32px; width: 100%; } #cw-wt-page .cw-wt-time { width: 100%; } } `; document.head .appendChild(style); } function svgClock(size = 16) { return ` `; } function svgPlus(size = 15) { return ` `; } function svgTrash(size = 15) { return ` `; } function svgX(size = 15) { return ` `; } function svgChevronLeft( size = 15 ) { return ` `; } function getMainArea() { const candidates = [ document.querySelector( '#app main' ), document.querySelector( '[role="main"]' ), document.querySelector( '.flex-1.h-full.overflow-auto' ), document.querySelector( '.w-full.overflow-auto' ), ].filter(Boolean); if (candidates.length) { return candidates .sort((a, b) => { const ar = a.getBoundingClientRect(); const br = b.getBoundingClientRect(); return ( br.width * br.height - ar.width * ar.height ); })[0]; } return ( document.querySelector( '#app' ) || document.body ); } function positionPageRoot() { if ( !STATE.pageRoot || !document.body.contains( STATE.pageRoot ) ) { return; } const main = getMainArea(); if (!main) { return; } const rect = main.getBoundingClientRect(); STATE.pageRoot.style.left = `${Math.max( 0, rect.left )}px`; STATE.pageRoot.style.top = `${Math.max( 0, rect.top )}px`; STATE.pageRoot.style.width = `${Math.max( 320, rect.width )}px`; STATE.pageRoot.style.height = `${Math.max( 320, rect.height )}px`; } function ensureToastRoot() { let root = document.getElementById( 'cw-wt-toast-root' ); if (!root) { root = document.createElement( 'div' ); root.id = 'cw-wt-toast-root'; document.body .appendChild(root); } return root; } function notify( message, type = 'info', duration = 3200 ) { const root = ensureToastRoot(); const toast = document.createElement( 'div' ); toast.className = `cw-wt-toast ${type}`; toast.textContent = String(message); root.appendChild(toast); setTimeout( () => toast.remove(), duration ); } function refreshMenuVisibility() { const item = document.getElementById( 'menu-item-work-schedule-v14' ); if (!item) { return; } item.style.display = canSeeModuleMenu() ? '' : 'none'; } /* * Aqui usamos a estrutura real * que você capturou no Chatwoot 4.16.2. * * Deixa Horários logo após Conta. */ function findSettingsReferenceItem() { const accountSettings = document.querySelector( 'li[name="Settings Account Settings"]' ); if (accountSettings) { return accountSettings; } const automation = document.querySelector( 'li[name="Settings Automation"]' ); if (automation) { return automation; } const allLis = [ ...document.querySelectorAll( 'li' ), ]; return ( allLis.find(li => { const text = ( li.textContent || '' ) .trim() .toLowerCase(); return ( text === 'conta' || text === 'account' ); }) || null ); } function injectSidebarMenuItem() { if (STATE.destroyed) { return; } let existing = document.getElementById( 'menu-item-work-schedule-v14' ); if (existing) { STATE.menuItem = existing; refreshMenuVisibility(); return; } const reference = findSettingsReferenceItem(); if ( !reference ?.parentElement ) { return; } const li = document.createElement( 'li' ); li.id = 'menu-item-work-schedule-v14'; li.setAttribute( 'name', 'Settings Work Schedules V14' ); /* * Mesma classe do LI * Conta / Agentes / Times. */ li.className = reference.className || ( 'py-0.5 ps-2 ms-3 ' + 'relative text-n-slate-11 ' + 'min-w-0 child-item' ); const anchor = document.createElement( 'a' ); anchor.href = '#'; anchor.title = STATE.isMaster ? 'Master de horários' : CONFIG.moduleLabel; /* * Mesmas classes do A nativo. */ anchor.className = 'flex h-8 items-center gap-2 ' + 'px-2 py-1 rounded-lg ' + 'ltr:hover:bg-gradient-to-r ' + 'rtl:hover:bg-gradient-to-l ' + 'from-transparent ' + 'via-n-slate-3/70 ' + 'to-n-slate-3/70 ' + 'group min-w-0'; /* * Mesma estrutura interna: * * size-4 * lucide * text-sm * hidden span final */ anchor.innerHTML = `
${escapeHtml( subtitle || '' )}
Crie um perfil para definir a jornada semanal e depois associe agentes ou administradores da conta.
Tente outro termo de busca.
Nenhum agente foi retornado pela API desta conta.
| Empresa | Account ID | Status | Acesso |
|---|---|---|---|
|
${escapeHtml(
company.company_name
)}
${escapeHtml(
company.timezone
)}
· aviso
${company.warning_minutes}
min
|
#${escapeHtml( company.account_id )} | ${ company.enabled ? 'Habilitado' : 'Desabilitado' } | |
|
Nenhuma conta encontrada.
|
|||
Você está fora do período permitido para este perfil de trabalho.