diff --git a/backend/static/css/components/base-app.css b/backend/static/css/components/base-app.css new file mode 100644 index 00000000..135168a2 --- /dev/null +++ b/backend/static/css/components/base-app.css @@ -0,0 +1,278 @@ +/** + * base-app.css + * Базовые стили приложения: layout, navbar, sidebar, кнопки, feed-элементы + */ + +:root { + --sidebar-w: 190px; + --rightbar-w: 350px; + --navbar-h: 60px; + --bs-primary: #007AFF; + --bs-link-color: var(--bs-primary); + --bs-link-hover-color: #0062CC; + + /* feed tokens */ + --feed-radius: 18px; + --feed-shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 10px 30px rgba(0, 0, 0, .08); + --feed-shadow-hover: 0 4px 10px rgba(0, 0, 0, .06), 0 16px 40px rgba(0, 0, 0, .12); +} + +[data-bs-theme="dark"] { + --bs-body-bg: #1C1C1E; + --bs-body-color: #F2F2F7; + --bs-border-color: #3A3A3C; + --bs-tertiary-bg: #2C2C2E; + --bs-primary: #0A84FF; + --bs-link-color: var(--bs-primary); + --bs-link-hover-color: #59AFFF; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + font-size: 17px; + padding-top: var(--navbar-h); + overflow-x: hidden; + background-color: var(--bs-body-bg); +} + +@supports(padding-top: env(safe-area-inset-top)) { + body { + padding-top: calc(var(--navbar-h) + env(safe-area-inset-top)); + } +} + +.btn { + border-radius: 10px; + min-height: 44px; + transition: background-color .3s, transform .1s; +} + +.btn:hover { + transform: scale(1.02); +} + +.form-control { + border-radius: 10px; + height: 44px; + font-size: 17px; +} + +.form-control:focus { + box-shadow: 0 0 0 2px rgba(var(--bs-primary-rgb), .25); + border-color: var(--bs-primary); +} + +.dropdown-menu { + border-radius: 13px; + box-shadow: 0 6px 24px rgba(0, 0, 0, .12); +} + +/* ───────────────────────────────────────────────────────────── + NAVBAR +────────────────────────────────────────────────────────────── */ +.app-navbar { + position: fixed; + inset: 0 0 auto 0; + z-index: 1030; + border-bottom: .5px solid var(--bs-border-color); +} + +/* ───────────────────────────────────────────────────────────── + LAYOUT +────────────────────────────────────────────────────────────── */ +.app-layout { + display: block; +} + +@media (min-width: 992px) { + .app-layout.with-sidebar.with-rightbar { + display: grid; + grid-template-columns: var(--sidebar-w) 1fr var(--rightbar-w); + align-items: start; + } + + .app-layout.with-sidebar:not(.with-rightbar) { + display: grid; + grid-template-columns: var(--sidebar-w) 1fr; + align-items: start; + } +} + +/* ───────────────────────────────────────────────────────────── + SIDEBAR +────────────────────────────────────────────────────────────── */ +#sidebar { + --bs-offcanvas-width: var(--sidebar-w); + border-radius: 0 13px 13px 0; +} + +@media (min-width: 992px) { + #sidebar.offcanvas-lg { + position: sticky; + top: var(--navbar-h); + height: calc(100dvh - var(--navbar-h)); + align-self: start; + overflow: auto; + border-right: .5px solid var(--bs-border-color); + background: var(--bs-body-bg); + transform: none !important; + visibility: visible !important; + } +} + +/* ───────────────────────────────────────────────────────────── + NOTIFICATION BELL +────────────────────────────────────────────────────────────── */ +.bell-wrapper { + position: relative; + line-height: 1; + display: inline-block; +} + +.bell-dot { + position: absolute; + top: -2px; + right: -2px; + width: 9px; + height: 9px; + border-radius: 999px; + background: var(--bs-danger); + color: #fff; + font-size: 7px; + line-height: 9px; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 0 0 1px rgba(255, 255, 255, .65); +} + +/* ───────────────────────────────────────────────────────────── + MOBILE FAB +────────────────────────────────────────────────────────────── */ +.calendar-fab { + position: fixed; + right: 1rem; + bottom: 1rem; + z-index: 1060; + width: 52px; + height: 52px; + border-radius: 9999px; + box-shadow: 0 8px 24px rgba(0, 0, 0, .18); +} + +/* ───────────────────────────────────────────────────────────── + BUTTONS & GHOST BUTTONS +────────────────────────────────────────────────────────────── */ +.btn-ghost { + background: color-mix(in srgb, var(--bs-body-bg) 94%, transparent); + color: var(--bs-body-color); + padding: .35rem .6rem; + border-radius: 999px; + font-size: .9rem; +} + +.btn-ghost:hover { + background: color-mix(in srgb, var(--bs-body-bg) 88%, transparent); +} + +.btn-ghost .bi { + font-size: 1rem; + margin-right: .35rem; + vertical-align: -2px; +} + +/* ───────────────────────────────────────────────────────────── + FEED ELEMENTS +────────────────────────────────────────────────────────────── */ +/* «Новые сотрудники» */ +.feed-new { + border-radius: var(--feed-radius); + border: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 10%); + background: var(--bs-body-bg); + box-shadow: var(--feed-shadow); + padding: .9rem 1rem; +} + +/* stretched-link не перекрывает футер/формы */ +.feed-body .stretched-link::after, +.feed-img .stretched-link::after { + z-index: 1; +} + +.collapse { + position: relative; + z-index: 2; +} + +/* подпись «Скрыть/число» у кнопки комментариев по aria-expanded */ +.btn-ghost .txt-close { + display: none; +} + +.btn-ghost[aria-expanded="true"] .txt-open { + display: none; +} + +.btn-ghost[aria-expanded="true"] .txt-close { + display: inline; +} + +/* ───────────────────────────────────────────────────────────── + ALPHA HEADERS (буквенные разделители) +────────────────────────────────────────────────────────────── */ +.alpha-header { + position: sticky; + top: calc(var(--navbar-h) + 8px); + z-index: 1; + background: color-mix(in srgb, var(--bs-body-bg) 92%, transparent); + backdrop-filter: blur(6px); + border: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 30%); + color: var(--bs-secondary-color); + display: inline-flex; + align-items: center; + gap: .5rem; + padding: .2rem .6rem; + border-radius: 999px; + font-weight: 700; + font-size: .85rem; + margin: .25rem 0 .5rem .25rem; +} + +.alpha-dot { + width: 6px; + height: 6px; + border-radius: 999px; + background: var(--bs-primary); + opacity: .6; +} + +/* ───────────────────────────────────────────────────────────── + ACTION BUTTONS +────────────────────────────────────────────────────────────── */ +.action-btn { + min-height: unset; + padding: .35rem .6rem; +} + +.action-btn .bi { + font-size: 1rem; +} + +/* ───────────────────────────────────────────────────────────── + TEXT UTILITIES +────────────────────────────────────────────────────────────── */ +/* Переносим длинные слова/ссылки, сохраняем переносы строк */ +.feed-text, +.feed-full { + overflow-wrap: anywhere; + width: 100%; + margin-right: 0; + padding-right: 0; +} + +/* чтобы кнопка «Подробнее» не была перекрыта stretched-link */ +.readmore-toggle { + position: relative; + z-index: 2; +} diff --git a/backend/static/css/components/ios-components.css b/backend/static/css/components/ios-components.css new file mode 100644 index 00000000..f42a41ce --- /dev/null +++ b/backend/static/css/components/ios-components.css @@ -0,0 +1,566 @@ +/** + * ios-components.css + * iOS-style компоненты: карточки, списки, аккордеоны, badges, кнопки-иконки + */ + +/* ───────────────────────────────────────────────────────────── + BASE CARD +────────────────────────────────────────────────────────────── */ +.ios-card { + border: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 10%); + border-radius: var(--feed-radius); + background: var(--bs-body-bg); + box-shadow: var(--feed-shadow); + transition: box-shadow .18s ease, transform .18s ease, border-color .18s ease; + overflow: hidden; +} + +.ios-card:hover { + box-shadow: var(--feed-shadow-hover); + transform: translateY(-1px); +} + +/* ───────────────────────────────────────────────────────────── + HEADER +────────────────────────────────────────────────────────────── */ +.ios-header { + position: sticky; + top: var(--navbar-h, 0px); + z-index: 5; + padding: .75rem 0; + backdrop-filter: saturate(180%) blur(12px); + background: color-mix(in srgb, var(--bs-body-bg) 85%, transparent); + border-bottom: none !important; +} + +.ios-title { + font-weight: 800; + letter-spacing: -.01em; +} + +.row.g-ios { + align-items: flex-start; +} + +/* ───────────────────────────────────────────────────────────── + AVATAR +────────────────────────────────────────────────────────────── */ +.ios-ava, +.ios-ava--ph { + display: grid; + place-items: center; + background: var(--bs-tertiary-bg); + color: var(--bs-secondary-color); + font-size: 3rem; + line-height: 1; + width: 100%; + height: 100%; + border-radius: 28px; + border: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 20%); +} + +.ava-wrap { + position: relative; + width: 132px; + height: 132px; + margin: 0 auto 12px; + display: block; +} + +/* ───────────────────────────────────────────────────────────── + ICON BUTTONS +────────────────────────────────────────────────────────────── */ +.btn-icon { + width: 36px; + height: 36px; + padding: 0; + border-radius: 9999px; + display: grid; + place-items: center; + border: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 30%); + background: color-mix(in srgb, var(--bs-body-bg) 94%, transparent); + color: var(--bs-body-color); +} + +.btn-icon:hover { + background: color-mix(in srgb, var(--bs-body-bg) 88%, transparent); +} + +/* Варианты окраски */ +.btn-icon--primary { + color: var(--bs-primary); + border-color: color-mix(in srgb, var(--bs-primary) 30%, var(--bs-border-color)); +} + +.btn-icon--primary:hover { + background: color-mix(in srgb, var(--bs-primary) 16%, var(--bs-body-bg)); +} + +.btn-icon--success { + color: var(--bs-success); + border-color: color-mix(in srgb, var(--bs-success) 30%, var(--bs-border-color)); +} + +.btn-icon--success:hover { + background: color-mix(in srgb, var(--bs-success) 16%, var(--bs-body-bg)); +} + +.btn-icon--danger { + color: var(--bs-danger); + border-color: color-mix(in srgb, var(--bs-danger) 30%, var(--bs-border-color)); +} + +.btn-icon--danger:hover { + background: color-mix(in srgb, var(--bs-danger) 16%, var(--bs-body-bg)); +} + +.btn-icon:focus-visible { + outline: 2px solid color-mix(in srgb, currentColor 60%, transparent); + outline-offset: 2px; +} + +.header-icon-group .btn-icon { + width: 36px; + height: 36px; +} + +/* ───────────────────────────────────────────────────────────── + NAME & ROLE +────────────────────────────────────────────────────────────── */ +.ios-name { + font-weight: 800; + font-size: 1.25rem; +} + +.ios-role { + font-size: .9rem; + color: var(--bs-secondary-color); +} + +/* ───────────────────────────────────────────────────────────── + CHIPS +────────────────────────────────────────────────────────────── */ +.chips { + display: flex; + flex-wrap: wrap; + gap: .5rem; +} + +.chip { + padding: .35rem .65rem; + border-radius: 9999px; + font-size: .85rem; + line-height: 1; + background: var(--bs-secondary-bg); + color: var(--bs-body-color); + border: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 20%); +} + +/* ───────────────────────────────────────────────────────────── + COLLAPSE TRIGGERS +────────────────────────────────────────────────────────────── */ +.ios-collapse-trigger { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + gap: .75rem; + padding: .6rem .9rem; + cursor: pointer; + border-radius: 14px; + border: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 20%); + background: color-mix(in srgb, var(--bs-body-bg) 94%, transparent); + color: var(--bs-body-color); +} + +.ios-collapse-caret { + transition: transform .25s ease; +} + +.ios-collapse-trigger[aria-expanded="true"] .ios-collapse-caret { + transform: rotate(180deg); +} + +.ios-collapse-trigger + .collapse { + margin-top: 0 !important; +} + +.ios-collapse-trigger + .collapse > .ios-list, +.ios-collapse-trigger + .collapse > .chips { + margin-top: .5rem; +} + +.collapsing { + overflow: hidden; + transition: height .35s ease; +} + +/* ───────────────────────────────────────────────────────────── + LISTS +────────────────────────────────────────────────────────────── */ +.ios-list { + border-radius: 14px; + overflow: hidden; + background: var(--bs-body-bg); + border: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 10%); +} + +.ios-cell { + display: flex; + align-items: center; + gap: .75rem; + padding: .85rem 1rem; + border-bottom: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 10%); +} + +.ios-cell:last-child { + border-bottom: 0; +} + +.ios-cell i { + width: 1.25rem; + text-align: center; + opacity: .9; +} + +.ios-cell a { + text-decoration: none; + color: inherit; +} + +.ios-section-title { + font-weight: 700; + font-size: 1rem; + margin-bottom: .5rem; +} + +.rounded-ios { + border-radius: 14px !important; +} + +/* ───────────────────────────────────────────────────────────── + DEPARTMENTS LIST +────────────────────────────────────────────────────────────── */ +.ios-cell.dept-item { + justify-content: space-between; +} + +.ios-cell.dept-head { + border-left: 0; + background: transparent; +} + +.dept-line { + display: flex; + align-items: center; + gap: .5rem; +} + +.dept-name { + font-weight: 700; +} + +.dept-role { + color: var(--bs-secondary-color); +} + +/* ───────────────────────────────────────────────────────────── + BADGES (HEAD & GEAR) +────────────────────────────────────────────────────────────── */ +.badge-head { + display: inline-flex; + align-items: center; + gap: .35rem; + padding: .2rem .55rem; + border-radius: 999px; + background: color-mix(in srgb, var(--bs-warning) 15%, var(--bs-body-bg)); + color: color-mix(in srgb, var(--bs-warning) 60%, black 0%); + border: 1px solid color-mix(in srgb, var(--bs-warning) 35%, transparent); + font-weight: 700; + font-size: .8rem; + line-height: 1; + white-space: nowrap; +} + +.badge-head .bi { + font-size: .95rem; + line-height: 1; +} + +.badge-gear { + display: inline-flex; + align-items: center; + gap: .35rem; + padding: .2rem .55rem; + border-radius: 999px; + cursor: pointer; + background: color-mix(in srgb, var(--bs-primary) 15%, var(--bs-body-bg)); + color: color-mix(in srgb, var(--bs-primary) 65%, black 0%); + border: 1px solid color-mix(in srgb, var(--bs-primary) 35%, transparent); + font-weight: 700; + font-size: .8rem; + line-height: 1; + white-space: nowrap; +} + +.badge-gear .bi { + font-size: .95rem; + line-height: 1; +} + +.badge-gear:hover { + background: color-mix(in srgb, var(--bs-primary) 20%, var(--bs-body-bg)); +} + +@media (max-width: 420px) { + .badge-head .lbl, + .badge-gear .lbl { + display: none; + } +} + +/* ───────────────────────────────────────────────────────────── + ACCORDION (IOS-ACC) +────────────────────────────────────────────────────────────── */ +.ios-acc { + border: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 20%); + border-radius: 14px; + background: var(--bs-body-bg); + overflow: hidden; +} + +.ios-acc + .ios-acc { + margin-top: .75rem; +} + +.ios-acc .ios-collapse-trigger { + border: 0; + border-radius: 0; + background: transparent; + width: 100%; + padding: .6rem .9rem; +} + +.ios-acc .ios-collapse-trigger:focus { + box-shadow: none; +} + +.ios-acc .collapse { + margin: 0 !important; +} + +.ios-acc .ios-list { + border: 0; + border-radius: 0; +} + +.ios-acc .chips { + margin: 0; +} + +.ios-acc .ios-acc-body { + padding: .5rem .25rem .75rem; + border-top: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 20%); +} + +.ios-acc .ios-collapse-trigger + .collapse { + margin-top: 0 !important; +} + +.ios-acc .ios-acc-body { + border-top: 0; +} + +/* ───────────────────────────────────────────────────────────── + ACTIVITY BLOCK +────────────────────────────────────────────────────────────── */ +#activity .p-4 { + padding: 0 !important; +} + +#activity .ios-section-title { + padding: 1rem 1rem .5rem !important; + margin: 0 !important; +} + +#activity .activity-feed { + margin: 0 !important; +} + +#activity .activity-feed .feed-card { + width: 100% !important; + max-width: none !important; + border: 0 !important; + box-shadow: none !important; + border-radius: 0 !important; + margin: 0 !important; +} + +#activity .activity-feed { + display: flex; + flex-direction: column; + gap: 1rem; +} + +#activity .activity-feed .feed-stream { + display: flex; + flex-direction: column; + gap: 1rem; +} + +#activity .activity-feed .row > * { + flex: 0 0 100% !important; + max-width: 100% !important; +} + +#activity .feed-actions { + border-top: 0 !important; +} + +#activity .collapse .bg-body-tertiary.border { + border: 0 !important; +} + +/* ───────────────────────────────────────────────────────────── + SKILLS UI +────────────────────────────────────────────────────────────── */ +#skillsBlock[data-editable="1"] .chip.chip-add { + display: inline-flex; + align-items: center; + cursor: pointer; + border-style: dashed; +} + +#skillsBlock[data-editable="1"] .chip.chip-add.pulse { + transform: scale(.97); + transition: transform .15s ease; +} + +#skillsBlock[data-editable="1"] .chip.chip-add .bi { + font-size: .95rem; +} + +.chip.chip-skill { + position: relative; + padding: .35rem .65rem; +} + +.chip.chip-skill .lbl { + transition: opacity .15s ease; +} + +.chip.chip-skill .chip-del-btn { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: 0; + padding: 0; + margin: 0; + cursor: pointer; + color: var(--bs-danger); + font-size: 1rem; + line-height: 1; + visibility: hidden; + opacity: 0; + transition: opacity .15s ease; + z-index: 1; +} + +.chip.chip-skill .chip-del-btn .bi { + pointer-events: none; +} + +#skillsBlock[data-editable="1"] .chip.chip-skill:hover .lbl { + opacity: 0; +} + +#skillsBlock[data-editable="1"] .chip.chip-skill:hover .chip-del-btn, +#skillsBlock[data-editable="1"] .chip.chip-skill:focus-within .chip-del-btn { + visibility: visible; + opacity: 1; +} + +.skills-empty { + color: var(--bs-secondary-color); + font-size: .875rem; + margin-right: .25rem; +} + +/* ───────────────────────────────────────────────────────────── + DEPT INLINE EDITOR +────────────────────────────────────────────────────────────── */ +.dept-edit-btn { + width: 32px; + height: 32px; + padding: 0; + display: grid; + place-items: center; + border-radius: 999px; + border: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 30%); + background: color-mix(in srgb, var(--bs-body-bg) 94%, transparent); +} + +.dept-edit-btn:hover { + background: color-mix(in srgb, var(--bs-body-bg) 88%, transparent); +} + +.dept-editor-wrap { + flex: 0 0 100%; + width: 100%; + margin-top: .5rem; +} + +.dept-editor form { + display: flex; + align-items: center; + gap: .5rem; +} + +.dept-role-select { + min-width: 180px; +} + +/* полностью прячем роль, когда открыт редактор */ +.dept-item.is-editing .dept-role { + display: none !important; +} + +/* Flex-управление элементами */ +.dept-item > .d-flex { + flex: 0 1 auto; + flex-wrap: wrap; + justify-content: flex-end; + gap: .35rem .5rem; + max-width: 100%; +} + +.dept-line { + min-width: 0; +} + +.badge-head, +.badge-gear { + flex: 0 0 auto; +} + +.ios-cell { + flex-wrap: wrap; +} + +@media (max-width: 576px) { + .badge-head .lbl, + .badge-gear .lbl { + display: none; + } +} + +/* ───────────────────────────────────────────────────────────── + CONTAINER +────────────────────────────────────────────────────────────── */ +.container-xxl { + max-width: 1820px; + padding-inline: var(--bs-gutter-x, .75rem); +} diff --git a/backend/templates/base.html b/backend/templates/base.html index 0863af58..4ee422fb 100644 --- a/backend/templates/base.html +++ b/backend/templates/base.html @@ -28,388 +28,8 @@ - - - - + + {% block extra_css %}{% endblock %}