Brings the Work Package Suite from a browser-local prototype to a multi-tenant, SQL-backed deployment hardened for customer IP. Auth & access control - Local username/password login (bcrypt + JWT in an HttpOnly cookie), admin-managed users, per-project membership, and project-scoped API access. - Admin console: change user roles, view the audit trail, manage settings. Security hardening - CSP / HSTS / X-Frame-Options / nosniff headers in nginx; Secure cookie via X-Forwarded-Proto; CSRF Origin check; attribute-safe output escaping. - Login lockout, token_version session revocation, stronger password policy, fail-closed secret loading, encrypted (AES-256) database backups. Persistence & schema - SOPs and Work Packages are now DB-backed and shared across users, written through a durable client sync outbox that queues offline edits. - Alembic migrations applied automatically on container start. New capabilities - Phase 2 dashboard (progress, gating, pagination, archive). - Phase 3 PWA "Field View" with offline caching and auth fallback. - WP owner assignment with OPTIONAL email notifications, OFF by default and toggled from the admin console. SMTP password is read only from the SMTP_PASSWORD env var (never stored); emails carry a WP number + deep link, never customer IP. Also: IBM Carbon restyle, Help section, and DEPLOYMENT.md brought up to date. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
250 lines
7.7 KiB
CSS
250 lines
7.7 KiB
CSS
/* IBM Carbon Design System - Light Theme (g10) */
|
|
:root {
|
|
--cds-interactive-01: #0f62fe;
|
|
--cds-interactive-02: #393939;
|
|
--cds-interactive-03: #0f62fe;
|
|
--cds-interactive-04: #0f62fe;
|
|
--cds-ui-background: #f4f4f4;
|
|
--cds-ui-01: #ffffff;
|
|
--cds-ui-02: #f4f4f4;
|
|
--cds-ui-03: #e0e0e0;
|
|
--cds-ui-04: #8d8d8d;
|
|
--cds-ui-05: #161616;
|
|
--cds-text-01: #161616;
|
|
--cds-text-02: #525252;
|
|
--cds-text-03: #a8a8a8;
|
|
--cds-text-04: #ffffff;
|
|
--cds-text-05: #6f6f6f;
|
|
--cds-text-error: #da1e28;
|
|
--cds-icon-01: #161616;
|
|
--cds-icon-02: #525252;
|
|
--cds-icon-03: #ffffff;
|
|
--cds-link-01: #0f62fe;
|
|
--cds-link-02: #0043ce;
|
|
--cds-inverse-link: #78a9ff;
|
|
--cds-field-01: #ffffff;
|
|
--cds-field-02: #f4f4f4;
|
|
--cds-inverse-01: #ffffff;
|
|
--cds-inverse-02: #393939;
|
|
--cds-support-01: #da1e28;
|
|
--cds-support-02: #198038;
|
|
--cds-support-03: #f1c21b;
|
|
--cds-support-04: #0043ce;
|
|
--cds-inverse-support-01: #fa4d56;
|
|
--cds-inverse-support-02: #42be65;
|
|
--cds-inverse-support-03: #f1c21b;
|
|
--cds-inverse-support-04: #4589ff;
|
|
--cds-overlay: rgba(22, 22, 22, .5);
|
|
--cds-danger: #da1e28;
|
|
--cds-focus: #0f62fe;
|
|
--cds-hover-primary: #0353e9;
|
|
--cds-active-primary: #002d9c;
|
|
--cds-hover-primary-text: #0043ce;
|
|
--cds-hover-secondary: #4c4c4c;
|
|
--cds-active-secondary: #6f6f6f;
|
|
--cds-hover-tertiary: #0353e9;
|
|
--cds-active-tertiary: #002d9c;
|
|
--cds-hover-ui: #e5e5e5;
|
|
--cds-hover-light-ui: #e5e5e5;
|
|
--cds-hover-selected-ui: #cacaca;
|
|
--cds-active-ui: #c6c6c6;
|
|
--cds-active-light-ui: #cacaca;
|
|
--cds-selected-ui: #e5e5e5;
|
|
--cds-selected-light-ui: #e5e5e5;
|
|
--cds-inverse-hover-ui: #353535;
|
|
--cds-hover-danger: #ba121b;
|
|
--cds-active-danger: #750e13;
|
|
--cds-hover-row: #f4f4f4;
|
|
--cds-visited-link: #0043ce;
|
|
--cds-disabled-01: #f4f4f4;
|
|
--cds-disabled-02: #bdbdbd;
|
|
--cds-disabled-03: #8d8d8d;
|
|
--cds-button-primary: #0f62fe;
|
|
--cds-button-secondary: #393939;
|
|
--cds-button-tertiary: #0f62fe;
|
|
--cds-button-danger-primary: #da1e28;
|
|
--cds-button-danger-secondary: #da1e28;
|
|
--cds-background: #f4f4f4;
|
|
--cds-background-inverse: #161616;
|
|
--cds-background-brand: #0f62fe;
|
|
--cds-background-active: #e5e5e5;
|
|
--cds-background-hover: #e8e8e8;
|
|
--cds-background-selected: #e5e5e5;
|
|
--cds-background-selected-hover: #cacaca;
|
|
--cds-layer: #ffffff;
|
|
--cds-layer-accent: #f4f4f4;
|
|
--cds-layer-accent-hover: #e8e8e8;
|
|
--cds-layer-accent-active: #e0e0e0;
|
|
--cds-layer-hover: #e8e8e8;
|
|
--cds-layer-active: #e5e5e5;
|
|
--cds-layer-selected: #e5e5e5;
|
|
--cds-layer-selected-hover: #cacaca;
|
|
--cds-layer-selected-inverse: #393939;
|
|
--cds-field: #ffffff;
|
|
--cds-field-hover: #e8e8e8;
|
|
--cds-border-subtle: #e0e0e0;
|
|
--cds-border-strong: #8d8d8d;
|
|
--cds-border-inverse: #393939;
|
|
--cds-border-interactive: #0f62fe;
|
|
--cds-border-subtle-selected: #c6c6c6;
|
|
--cds-text-primary: #161616;
|
|
--cds-text-secondary: #525252;
|
|
--cds-text-placeholder: #a8a8a8;
|
|
--cds-text-helper: #6f6f6f;
|
|
--cds-text-on-color: #ffffff;
|
|
--cds-text-inverse: #f4f4f4;
|
|
--cds-text-disabled: #bdbdbd;
|
|
--cds-link-primary: #0f62fe;
|
|
--cds-link-secondary: #0043ce;
|
|
--cds-link-visited: #0043ce;
|
|
--cds-link-inverse: #78a9ff;
|
|
--cds-link-primary-hover: #0353e9;
|
|
--cds-icon-primary: #161616;
|
|
--cds-icon-secondary: #525252;
|
|
--cds-icon-on-color: #ffffff;
|
|
--cds-icon-inverse: #f4f4f4;
|
|
--cds-icon-disabled: #bdbdbd;
|
|
--cds-support-error: #da1e28;
|
|
--cds-support-success: #198038;
|
|
--cds-support-warning: #f1c21b;
|
|
--cds-support-info: #0043ce;
|
|
--cds-support-error-inverse: #fa4d56;
|
|
--cds-support-success-inverse: #42be65;
|
|
--cds-support-warning-inverse: #f1c21b;
|
|
--cds-support-info-inverse: #4589ff;
|
|
--cds-interactive: #0f62fe;
|
|
--cds-shadow: rgba(0, 0, 0, .16);
|
|
}
|
|
|
|
/* Typography */
|
|
body {
|
|
font-family: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
|
|
color: var(--cds-text-primary);
|
|
background: var(--cds-background);
|
|
font-size: 1rem;
|
|
font-weight: 400;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
h1, h2, h3, h4, h5, h6 {
|
|
font-weight: 600;
|
|
line-height: 1.28572;
|
|
}
|
|
|
|
h1 { font-size: 2rem; }
|
|
h2 { font-size: 1.75rem; }
|
|
h3 { font-size: 1.25rem; }
|
|
h4 { font-size: 1rem; }
|
|
h5 { font-size: .875rem; }
|
|
h6 { font-size: .75rem; }
|
|
|
|
a {
|
|
color: var(--cds-link-primary);
|
|
text-decoration: none;
|
|
}
|
|
|
|
a:hover {
|
|
color: var(--cds-link-primary-hover);
|
|
text-decoration: underline;
|
|
}
|
|
|
|
button {
|
|
font-family: inherit;
|
|
cursor: pointer;
|
|
}
|
|
|
|
input, textarea, select {
|
|
font-family: inherit;
|
|
color: var(--cds-text-primary);
|
|
}
|
|
|
|
/* ============================================================================
|
|
App shell — shared "UI Shell" chrome (Prime Controls, IBM Carbon styling)
|
|
----------------------------------------------------------------------------
|
|
One dark top bar across every page so the suite reads as a single product.
|
|
The Prime Controls logo is a white-background wordmark, so it sits inside a
|
|
white "chip" on the near-black bar (reads as intentional, not a stray box).
|
|
Flip --wp-appbar-bg to a light value if a light header is ever preferred.
|
|
============================================================================ */
|
|
:root {
|
|
--wp-appbar-bg: #161616; /* near-black UI Shell bar */
|
|
--wp-appbar-fg: #ffffff;
|
|
--wp-appbar-fg-dim: #c6c6c6;
|
|
--wp-appbar-border: #6f6f6f; /* outline for ghost buttons on the bar */
|
|
--wp-appbar-hover: #353535;
|
|
--wp-appbar-height: 48px;
|
|
}
|
|
|
|
.wp-appbar {
|
|
background: var(--wp-appbar-bg);
|
|
color: var(--wp-appbar-fg);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
height: var(--wp-appbar-height);
|
|
padding: 0 16px;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
}
|
|
.wp-appbar-brand {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
height: 100%;
|
|
text-decoration: none;
|
|
color: var(--wp-appbar-fg);
|
|
}
|
|
.wp-appbar-brand:hover { text-decoration: none; opacity: .92; }
|
|
.wp-logo-chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: #fff;
|
|
border-radius: 4px;
|
|
padding: 4px 8px;
|
|
}
|
|
.wp-logo-chip img { height: 24px; width: auto; display: block; }
|
|
.wp-appbar-title {
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: var(--wp-appbar-fg);
|
|
white-space: nowrap;
|
|
letter-spacing: .01em;
|
|
}
|
|
.wp-appbar-title .wp-appbar-sub { font-weight: 400; color: var(--wp-appbar-fg-dim); }
|
|
.wp-appbar-spacer { flex: 1 1 auto; }
|
|
.wp-appbar-meta { font-size: 13px; color: var(--wp-appbar-fg-dim); white-space: nowrap; }
|
|
.wp-appbar-actions { display: flex; align-items: center; gap: 8px; }
|
|
|
|
/* Buttons and links that live on the dark bar */
|
|
.wp-appbar-btn {
|
|
background: transparent;
|
|
color: var(--wp-appbar-fg);
|
|
border: 1px solid var(--wp-appbar-border);
|
|
border-radius: 0;
|
|
padding: 7px 14px;
|
|
font-size: 14px;
|
|
font-family: inherit;
|
|
line-height: 1.2;
|
|
cursor: pointer;
|
|
text-decoration: none;
|
|
white-space: nowrap;
|
|
transition: background .15s, border-color .15s;
|
|
}
|
|
.wp-appbar-btn:hover { background: var(--wp-appbar-hover); color: var(--wp-appbar-fg); text-decoration: none; }
|
|
.wp-appbar-btn.primary { background: var(--cds-interactive-01); border-color: var(--cds-interactive-01); }
|
|
.wp-appbar-btn.primary:hover { background: var(--cds-hover-primary); border-color: var(--cds-hover-primary); }
|
|
.wp-appbar-btn:focus-visible { outline: 2px solid var(--wp-appbar-fg); outline-offset: 1px; }
|
|
.wp-appbar-count { font-size: 13px; color: var(--wp-appbar-fg-dim); padding: 0 2px; white-space: nowrap; }
|
|
|
|
/* Plain text links on the dark bar (Overview / Feedback / Help, Admin, etc.) */
|
|
.wp-appbar-link { color: var(--wp-appbar-fg-dim); text-decoration: none; font-size: 14px; white-space: nowrap; }
|
|
.wp-appbar-link:hover { color: var(--wp-appbar-fg); text-decoration: none; }
|
|
|
|
@media (max-width: 720px) {
|
|
.wp-appbar { height: auto; flex-wrap: wrap; gap: 8px; padding: 8px 12px; }
|
|
.wp-appbar-actions { flex-wrap: wrap; }
|
|
.wp-appbar-meta { width: 100%; order: 5; }
|
|
}
|