Implements the schedule module for COMPASS construction PM: - D1/Drizzle schema: tasks, dependencies, phases tables - frappe-gantt integration for interactive timeline view - Critical path analysis (forward/backward pass, float calc) - Dependency validation with cycle detection - Business day calculations (skip weekends/holidays) - Date propagation engine for cascading schedule changes - Task CRUD with phase assignment and progress tracking - Dependency management (FS/FF/SS/SF with lag support) - Dual view: sortable list view + gantt chart view Also includes full Next.js app scaffold with dashboard, shadcn/ui components, and Cloudflare Workers deployment config.
164 lines
8.3 KiB
CSS
Executable File
164 lines
8.3 KiB
CSS
Executable File
/* frappe-gantt base styles (vendored from frappe-gantt/dist/frappe-gantt.css) */
|
|
:root{--g-arrow-color: #1f2937;--g-bar-color: #fff;--g-bar-border: #fff;--g-tick-color-thick: #ededed;--g-tick-color: #f3f3f3;--g-actions-background: #f3f3f3;--g-border-color: #ebeff2;--g-text-muted: #7c7c7c;--g-text-light: #fff;--g-text-dark: #171717;--g-progress-color: #dbdbdb;--g-handle-color: #37352f;--g-weekend-label-color: #dcdce4;--g-expected-progress: #c4c4e9;--g-header-background: #fff;--g-row-color: #fdfdfd;--g-row-border-color: #c7c7c7;--g-today-highlight: #37352f;--g-popup-actions: #ebeff2;--g-weekend-highlight-color: #f7f7f7}
|
|
.gantt-container{line-height:14.5px;position:relative;overflow:auto;font-size:12px;height:var(--gv-grid-height);width:100%;border-radius:8px}
|
|
.gantt-container .popup-wrapper{position:absolute;top:0;left:0;background:#fff;box-shadow:0 10px 24px -3px #0003;padding:10px;border-radius:5px;width:max-content;z-index:1000}
|
|
.gantt-container .popup-wrapper .title{margin-bottom:2px;color:var(--g-text-dark);font-size:.85rem;font-weight:650;line-height:15px}
|
|
.gantt-container .popup-wrapper .subtitle{color:var(--g-text-dark);font-size:.8rem;margin-bottom:5px}
|
|
.gantt-container .popup-wrapper .details{color:var(--g-text-muted);font-size:.7rem}
|
|
.gantt-container .popup-wrapper .actions{margin-top:10px;margin-left:3px}
|
|
.gantt-container .popup-wrapper .action-btn{border:none;padding:5px 8px;background-color:var(--g-popup-actions);border-right:1px solid var(--g-text-light)}
|
|
.gantt-container .popup-wrapper .action-btn:hover{background-color:brightness(97%)}
|
|
.gantt-container .popup-wrapper .action-btn:first-child{border-top-left-radius:4px;border-bottom-left-radius:4px}
|
|
.gantt-container .popup-wrapper .action-btn:last-child{border-right:none;border-top-right-radius:4px;border-bottom-right-radius:4px}
|
|
.gantt-container .grid-header{height:calc(var(--gv-lower-header-height) + var(--gv-upper-header-height) + 10px);background-color:var(--g-header-background);position:sticky;top:0;left:0;border-bottom:1px solid var(--g-row-border-color);z-index:1000}
|
|
.gantt-container .lower-text,.gantt-container .upper-text{text-anchor:middle}
|
|
.gantt-container .upper-header{height:var(--gv-upper-header-height)}
|
|
.gantt-container .lower-header{height:var(--gv-lower-header-height)}
|
|
.gantt-container .lower-text{font-size:12px;position:absolute;width:calc(var(--gv-column-width) * .8);height:calc(var(--gv-lower-header-height) * .8);margin:0 calc(var(--gv-column-width) * .1);align-content:center;text-align:center;color:var(--g-text-muted)}
|
|
.gantt-container .upper-text{position:absolute;width:fit-content;font-weight:500;font-size:14px;color:var(--g-text-dark);height:calc(var(--gv-lower-header-height) * .66)}
|
|
.gantt-container .current-upper{position:sticky;left:0!important;padding-left:17px;background:#fff}
|
|
.gantt-container .side-header{position:sticky;top:0;right:0;float:right;z-index:1000;line-height:20px;font-weight:400;width:max-content;margin-left:auto;padding-right:10px;padding-top:10px;background:var(--g-header-background);display:flex}
|
|
.gantt-container .side-header *{transition-property:background-color;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;background-color:var(--g-actions-background);border-radius:.5rem;border:none;padding:5px 8px;color:var(--g-text-dark);font-size:14px;letter-spacing:.02em;font-weight:420;box-sizing:content-box;margin-right:5px}
|
|
.gantt-container .side-header *:last-child{margin-right:0}
|
|
.gantt-container .side-header *:hover{filter:brightness(97.5%)}
|
|
.gantt-container .side-header select{width:60px;padding-top:2px;padding-bottom:2px}
|
|
.gantt-container .side-header select:focus{outline:none}
|
|
.gantt-container .date-range-highlight{background-color:var(--g-progress-color);border-radius:12px;height:calc(var(--gv-lower-header-height) - 6px);top:calc(var(--gv-upper-header-height) + 5px);position:absolute}
|
|
.gantt-container .current-highlight{position:absolute;background:var(--g-today-highlight);width:1px;z-index:999}
|
|
.gantt-container .current-ball-highlight{position:absolute;background:var(--g-today-highlight);z-index:1001;border-radius:50%}
|
|
.gantt-container .current-date-highlight{background:var(--g-today-highlight);color:var(--g-text-light);border-radius:5px}
|
|
.gantt-container .holiday-label{position:absolute;top:0;left:0;opacity:0;z-index:1000;background:--g-weekend-label-color;border-radius:5px;padding:2px 5px}
|
|
.gantt-container .holiday-label.show{opacity:100}
|
|
.gantt-container .extras{position:sticky;left:0}
|
|
.gantt-container .hide{display:none}
|
|
.gantt{user-select:none;-webkit-user-select:none;position:absolute}
|
|
.gantt .grid-background{fill:none}
|
|
.gantt .grid-row{fill:var(--g-row-color)}
|
|
.gantt .row-line{stroke:var(--g-border-color)}
|
|
.gantt .tick{stroke:var(--g-tick-color);stroke-width:.4}
|
|
.gantt .tick.thick{stroke:var(--g-tick-color-thick);stroke-width:.7}
|
|
.gantt .arrow{fill:none;stroke:var(--g-arrow-color);stroke-width:1.5}
|
|
.gantt .bar-wrapper .bar{fill:var(--g-bar-color);stroke:var(--g-bar-border);stroke-width:0;transition:stroke-width .3s ease}
|
|
.gantt .bar-progress{fill:var(--g-progress-color);border-radius:4px}
|
|
.gantt .bar-expected-progress{fill:var(--g-expected-progress)}
|
|
.gantt .bar-invalid{fill:transparent;stroke:var(--g-bar-border);stroke-width:1;stroke-dasharray:5}
|
|
:is(.gantt .bar-invalid)~.bar-label{fill:var(--g-text-light)}
|
|
.gantt .bar-label{fill:var(--g-text-dark);dominant-baseline:central;font-family:Helvetica;font-size:13px;font-weight:400}
|
|
.gantt .bar-label.big{fill:var(--g-text-dark);text-anchor:start}
|
|
.gantt .handle{fill:var(--g-handle-color);opacity:0;transition:opacity .3s ease}
|
|
.gantt .handle.active,.gantt .handle.visible{cursor:ew-resize;opacity:1}
|
|
.gantt .handle.progress{fill:var(--g-text-muted)}
|
|
.gantt .bar-wrapper{cursor:pointer}
|
|
.gantt .bar-wrapper .bar{outline:1px solid var(--g-row-border-color);border-radius:3px}
|
|
.gantt .bar-wrapper:hover .bar{transition:transform .3s ease}
|
|
|
|
/* theme overrides */
|
|
.gantt-container {
|
|
--gantt-bar-color: oklch(0.7 0.12 170);
|
|
--gantt-bar-stroke: oklch(0.6 0.12 170);
|
|
--gantt-critical-color: oklch(0.65 0.2 25);
|
|
--gantt-milestone-color: oklch(0.7 0.15 280);
|
|
}
|
|
|
|
.gantt .bar-wrapper .bar {
|
|
fill: var(--gantt-bar-color);
|
|
stroke: var(--gantt-bar-stroke);
|
|
stroke-width: 0.5;
|
|
rx: 4;
|
|
ry: 4;
|
|
}
|
|
|
|
.gantt .bar-wrapper .bar-progress {
|
|
fill: oklch(0.55 0.12 170);
|
|
}
|
|
|
|
.gantt .bar-wrapper.critical-path .bar {
|
|
fill: var(--gantt-critical-color);
|
|
stroke: oklch(0.55 0.2 25);
|
|
}
|
|
|
|
.gantt .bar-wrapper.critical-path .bar-progress {
|
|
fill: oklch(0.5 0.2 25);
|
|
}
|
|
|
|
.gantt .bar-wrapper.milestone .bar {
|
|
fill: var(--gantt-milestone-color);
|
|
stroke: oklch(0.6 0.15 280);
|
|
}
|
|
|
|
.gantt .bar-label {
|
|
font-family: var(--font-sans);
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.gantt .grid-header {
|
|
fill: oklch(0.97 0 0);
|
|
}
|
|
|
|
.dark .gantt .grid-header {
|
|
fill: oklch(0.15 0 0);
|
|
}
|
|
|
|
.gantt .grid-row {
|
|
fill: transparent;
|
|
}
|
|
|
|
.gantt .grid-row:nth-child(even) {
|
|
fill: oklch(0.97 0.005 170 / 0.3);
|
|
}
|
|
|
|
.dark .gantt .grid-row:nth-child(even) {
|
|
fill: oklch(0.2 0.005 170 / 0.2);
|
|
}
|
|
|
|
.gantt .row-line {
|
|
stroke: oklch(0.9 0 0);
|
|
}
|
|
|
|
.dark .gantt .row-line {
|
|
stroke: oklch(0.25 0 0);
|
|
}
|
|
|
|
.gantt .tick {
|
|
stroke: oklch(0.92 0 0);
|
|
}
|
|
|
|
.dark .gantt .tick {
|
|
stroke: oklch(0.22 0 0);
|
|
}
|
|
|
|
.gantt .lower-text,
|
|
.gantt .upper-text {
|
|
font-family: var(--font-sans);
|
|
fill: oklch(0.5 0 0);
|
|
}
|
|
|
|
.dark .gantt .lower-text,
|
|
.dark .gantt .upper-text {
|
|
fill: oklch(0.65 0 0);
|
|
}
|
|
|
|
.gantt .arrow {
|
|
stroke: oklch(0.6 0.08 170);
|
|
stroke-width: 1.5;
|
|
}
|
|
|
|
.gantt .today-highlight {
|
|
fill: oklch(0.85 0.08 170 / 0.15);
|
|
}
|
|
|
|
/* phase-specific bar colors */
|
|
.gantt .bar-wrapper.phase-foundation .bar { fill: oklch(0.7 0.12 240); }
|
|
.gantt .bar-wrapper.phase-framing .bar { fill: oklch(0.75 0.14 50); }
|
|
.gantt .bar-wrapper.phase-electrical .bar { fill: oklch(0.8 0.15 85); }
|
|
.gantt .bar-wrapper.phase-plumbing .bar { fill: oklch(0.7 0.14 150); }
|
|
.gantt .bar-wrapper.phase-hvac .bar { fill: oklch(0.7 0.14 300); }
|
|
.gantt .bar-wrapper.phase-finish .bar { fill: oklch(0.75 0.14 340); }
|
|
.gantt .bar-wrapper.phase-roofing .bar { fill: oklch(0.6 0.08 40); }
|
|
.gantt .bar-wrapper.phase-sitework .bar { fill: oklch(0.7 0.12 70); }
|
|
.gantt .bar-wrapper.phase-insulation .bar { fill: oklch(0.75 0.1 10); }
|
|
.gantt .bar-wrapper.phase-drywall .bar { fill: oklch(0.75 0.03 0); }
|
|
.gantt .bar-wrapper.phase-landscaping .bar { fill: oklch(0.7 0.15 160); }
|
|
.gantt .bar-wrapper.phase-closeout .bar { fill: oklch(0.65 0.12 270); }
|