Lalabase
티켓·문서·근무시간·고객대화를 한 곳에 모아 KI로 검색, 소프트웨어 기업 고객 프로젝트용, 오픈소스로 자체 서버·독일 클라우드에 배포
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기0XNatasim/Sparklog
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
SparkLog is a bilingual, mobile-first time-tracking application for Québec electrical contractors. Employees record daily jobs and supporting evidence; managers review work, manage employee settings, approve time sheets, and prepare CCQ-oriented weekly exports. get_my_role() collapses admin and owner to manager for authorization; the raw role drives labels, pay basis, and privilege. Only an owner can assign roles (enforced by a DB trigger and an owner-only picker in Manager → Employees).README 전문 보기 ↗
SparkLog is a bilingual, mobile-first time-tracking application for Québec electrical contractors. Employees record daily jobs and supporting evidence; managers review work, manage employee settings, approve time sheets, and prepare CCQ-oriented weekly exports.
Documentation current as of V2.4.205 (2026-10-29).
README.md,NOOB.md(plain-language to-do list) andCLAUDE.md(detailed tracker) are updated together in every version;src/lib/docs-sync.test.jsfails when one of them does not name the currentAPP_VERSION. NOOB.md and CLAUDE.md list open items only (V2.4.195 removed every finished entry; the history is in Git); this README describes what the app does.
Roles are stored lowercase in profiles.role:
| Role | Access |
|---|---|
employee | Records and submits their own jobs; sees only their own data. |
subcontractor_1 | Sous-traitant-1: uses the employee time-entry workflow but is excluded from DAS payroll calculations and CCQ reports. |
manager | Full manager workspace: review, approve, export, employee settings, costing. |
admin | Administration/office staff. Manager-tier access to every screen except NAS/SIN, but non-CCQ: paid a flat hourly rate, and excluded from CCQ advantages (no leave indemnity, employer contributions, or meal allowance in costing). |
owner | The company owner — manager-tier and privileged (NAS/SIN reveal, role assignment, crown). Replaces the former hard-coded owner/dev accounts, so a fork just marks one account owner. |
get_my_role() collapses admin and owner to manager for authorization; the raw role drives labels, pay basis, and privilege. Only an owner can assign roles (enforced by a DB trigger and an owner-only picker in Manager → Employees).
compagnon, CCQ schedule C3 (general daytime work), a
$0.65/km mileage rate, and a $7/week cell-data reimbursement; managers can
adjust these values per employee.* = a draft day is
not counted), « arrondi ±n min · compteur ±c min ». The squares keep the time entered, to the minute. Paie de la semaine
has an « Arrondi » column: the week's difference and the counter at the end of the week, next to the hours (already
rounded). Same counter as Sommaire, talons and the printed summary (weekRoundingSummary, day-rounding.js).accident-files bucket, readable by managers only (signed links, 1 h); a manager can
remove one after a confirmation. Adding and removing a file is written to the Journal; the card shows the number of files.The Manager workspace is a sidebar (a drawer on phones; on desktop it can be shrunk to an icon rail or hidden
entirely with the toggle at the top left of the header); every page has its own URL (#/manager/…) and only the
pages a manager is allowed to open are listed. Since V2.4.186 there is one menu (the former « V3 »): the V2 menu and
the V2 | V3 selector are gone, and old V2 addresses (bookmarks, old links) open the matching page and tab.
Menu (15 entries; pages with tabs keep each tab's original access rule, the active tab is in ?tab=):
| Group | Entries [tabs] |
|---|---|
| Today (open) | To do [To approve · Receipts & proofs] · Live crew · Notes (private or shared with managers, optional reminder: red badge on « Notes » when due) |
| Time | Timesheets [Timesheets · Unlock a day · Days without entry] — employee name first on each card, « Entered by hand » ★ filter |
| Payroll (open) | Weekly payroll · Payroll history [Pay stubs · DAS · Record of employment · Detailed calculation] |
| Team | Employees [Employees (one card with tabs: identity, CCQ, pay and options, taxes, time off, access) · Time off · Groups] · Inventory · Communications |
| Reports | Reports [Costs · Week / Month · Summary · CCQ export] |
| Configuration | Configuration [Settings · CCQ rates · Pay rules · Forms · References · Contacts (with roles)] |
| Admin | Travel and expenses (per-employee rules, not yet read by payroll) · Activity log · Service status · Emergency timesheet (owner only) |
The live crew pauses its polling while the browser tab is hidden; CCQ weeks run Sunday→Saturday.
Managers can also broadcast announcements, synchronize CCQ rates, view the app as any employee, and force a manual refresh from the header (useful in installed PWA/App mode).
Other manager features worth knowing (details and versions in CLAUDE.md §6.3):
push_enabled_users, migration 0113).The current deployment is intentionally limited to:
220)I in exports; C for the CCQ wage-rate endpoint)Approved daily jobs are preserved as source records. The Download section aggregates them by employee, Saturday week-ending date, trade, sector, region, and appendix, with separate regular, 50% overtime, and 100% overtime totals.
The JSON exporter is an integration-ready internal format, not a guarantee of direct CCQ acceptance. Validate overtime, statutory-holiday, appendix, and submission rules before production payroll use.
SparkLog does not use an LLM, OpenRouter, Claude, GPT, or another generative-AI service for image extraction.
Work-order autofill sends the selected image to ocr.space from the browser and falls back to local tesseract.js processing if necessary. SparkLog parses the extracted work-order text into job fields.
The former LLM/Vision Edge Function has been removed. No OPENROUTER_API_KEY, ANTHROPIC_API_KEY, or other LLM credential is needed.
Parking receipt pictures and overtime proof screenshots are stored as images without text extraction. Overtime screenshots are removed according to the configured evidence-retention period.
dayjs for date/week calculationsnpm run lint, run in CI, no warning allowed), Vitest and Testing Library (screen tests in jsdom)src/
components/ Shared UI and manager panels (LiveCrew, JobCard, ErrorBoundary, …)
contexts/ Authentication/profile state
lib/ CCQ export/rate helpers, i18n, job contract, time helpers
pages/ Employee pages and the lazily loaded manager pages
payroll/ DAS test-bench engine, CCQ benefit layer, 2026 rule constants
supabase/
functions/ Server-side integrations (the shared payroll engine lives in _shared/)
migrations/ Incremental database, RLS, Storage, and cron changes
docs/ ADRs, payroll rules and sources, security matrix, ops notes
tests/ Official reference PDFs (CCQ tables, T4127, TP-1015.F) and the RLS suite
public/ Static images (WebP) and the push service worker
No LLM account or API key is required.
Install dependencies:
npm install
Create .env.local:
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key
VITE_OCR_SPACE_API_KEY=your-ocr-space-key
The process_overtime_evidence Edge Function also expects an OCR_SPACE_API_KEY Supabase secret for background overtime processing.
VITE_OCR_SPACE_API_KEY is technically optional because the code uses ocr.space's public helloworld key when absent, but that key is heavily rate-limited and should not be used for production.
Start the application:
npm run dev
Check and build (the same three steps CI runs on every pull request):
npm run lint # ESLint — 0 errors and 0 warnings (--max-warnings=0)
npm test # Vitest — payroll parity fixtures, WCAG theme contrast, screen tests (*.test.jsx, jsdom)
npm run build
The Vite development server normally runs at http://localhost:5173.
SparkLog expects Supabase Auth plus the base profiles and jobs tables. Existing installations should apply every migration in supabase/migrations in filename order. Each file is named <version>_<name>.sql with the version recorded in the production migration history; name new migrations with a UTC timestamp version (YYYYMMDDHHMMSS_name.sql) so they sort after existing ones. The migrations add and configure:
With the Supabase CLI linked to the target project:
supabase login
supabase link --project-ref YOUR_PROJECT_REF
supabase db push
For a fresh project, create the base schema and policies first or restore them from an existing SparkLog database before running these incremental migrations. At minimum:
profiles.id must reference auth.users.id and include role, full_name, email, and phone.jobs must include the employee, date, job fields, status, locked/export state, and timestamps.public.get_my_role() must return the authenticated profile role.Employee jobs policies (create a draft, create an already-submitted job, or move an
unlocked draft to the locked submitted state) are installed by the migrations, and
migration 0018_paused_employee_write_containment.sql additionally blocks all
employee writes while an account is paused and replaces the profile-field blacklist with
an explicit employee-editable whitelist. Migrations are written to be safe to re-run.
After migrations, set the first owner manually (the owner is the privileged tier and the only role that can assign other roles, so bootstrap it directly in SQL):
update public.profiles
set role = 'owner'
where email = '[email protected]';
Use role = 'manager' for additional supervisors, role = 'admin' for non-CCQ
administration staff, and role = 'subcontractor_1' for subcontractors who enter time
but must stay outside DAS and CCQ reports. Once an owner exists, roles can be assigned
from Manager → Employees.
Migrations create two private buckets:
overtime-evidenceparking-receiptsEmployees can upload only under their own user-ID folder. Managers receive read access through RLS and the UI creates short-lived signed URLs only when an image is requested. Do not make either bucket public.
Deploy the functions used by your environment:
supabase functions deploy push_approved_to_sheet
supabase functions deploy push_approved_batch
supabase functions deploy send_announcement
supabase functions deploy ccq_rates
supabase functions deploy ccq_rates_daily_sync
supabase functions deploy cleanup_overtime_evidence
supabase functions deploy create_employee
supabase functions deploy reset_employee_password
supabase functions deploy delete_user
supabase functions deploy send_push
supabase functions deploy infra_metrics
supabase functions deploy process_inventory_screenshot
supabase functions deploy delete_inventory_screenshots
| Function | Purpose |
|---|---|
push_approved_to_sheet | Approves/exports one job through Google Apps Script. |
push_approved_batch | Approves/exports a group of jobs. |
send_announcement | Persists and emails manager announcements. |
ccq_rates | Authenticated proxy/cache for CCQ commercial electrician rates. |
ccq_rates_daily_sync | Service-role-only refresh of commercial rate snapshots. |
classify_week | Authoritative payroll classification (M4): reads an employee's jobs server-side and returns the versioned computeWeek() result (per-job trace, per-week regular/OT50/OT100, engine version, warnings). Manager-only. Shares one engine with the client via supabase/functions/_shared/payroll_engine.js (parity-tested). |
process_overtime_evidence | Background OCR of overtime authorization screenshots (ocr.space). |
cleanup_overtime_evidence | Service-role-only deletion of expired overtime images and records. |
delete_user | Manager-only hard delete of an inactive account, with guards and an audit-log entry. |
create_employee | Manager/owner creation of an already-confirmed employee account with an optional generated temporary password. |
reset_employee_password | Manager/owner generation of a new one-time-displayed temporary password without deleting payroll history. |
send_push | Web push for announcements (and the « Message modifié » re-read push). |
infra_metrics | Manager-only service status (database, storage, connections). |
process_inventory_screenshot | Server-side reading (OCR) of inventory screenshots into equipment lines, including defective products and negative quantities. |
delete_inventory_screenshots | Manager removal of an inventory screenshot (file, lines and row together). |
Every manager-level function also checks the two-factor (aal2) claim (_shared/mfa.js).
The frontend sends authenticated bearer tokens to user-facing functions. Keep normal JWT verification/authentication behavior aligned with each function's own authorization checks; do not expose service-role credentials to the browser.
Supabase injects SUPABASE_URL, SUPABASE_ANON_KEY, and SUPABASE_SERVICE_ROLE_KEY. Configure only the integrations you use:
| Secret | Required for |
|---|---|
APPS_SCRIPT_URL | Google Sheets job export |
APPS_SCRIPT_TOKEN | Shared authentication secret for Apps Script |
RESEND_API_KEY + RESEND_FROM | Resend announcement email provider |
BREVO_API_KEY + BREVO_FROM_EMAIL | Brevo announcement email provider |
BREVO_FROM_NAME | Optional Brevo sender name |
CCQ_RATES_CACHE_TTL_HOURS | Optional CCQ cache override |
CCQ_RATES_REQUEST_TIMEOUT_MS | Optional CCQ request-timeout override |
If neither email provider is configured, announcements use the built-in mock provider: records are created, but no real email is sent.
The approval functions expect a Google Apps Script web app that:
APPS_SCRIPT_TOKEN.Store its deployed URL and shared token as Supabase secrets. Never put the Apps Script token in a VITE_ variable because Vite variables are public in the browser bundle.
npm run build and output directory dist..env.local.vercel.json supplies SPA rewrites and cache headers.
Connect the repository as a static site. render.yaml uses npm run build, publishes dist, and rewrites application routes to index.html.
Rules are recorded as data in the public.payroll_rules table and documented in
docs/rules/compensation-rules.md (with their CCQ sources). Highlights:
company_holidays).docs/rules/compensation-rules.md, DAY_ROUNDING).employer_contributions). It stays a labeled
estimate (statutory annual maximums are not modelled). admin (non-CCQ) staff are costed
at wages + km/parking only — no indemnity, contributions, or meal allowance.Every build emits /version.json (generated from APP_VERSION in src/lib/version.js,
never precached by the service worker). UpdateBanner checks it on load, on focus and
every 5 minutes and offers a Rafraîchir maintenant button. In addition, main.jsx
applies a newly installed service worker only when the tab goes to the background, so a
form in progress is never reloaded under the user. Pages are code-split: if a deploy
removed a file an open tab still needs, the app reloads once (guarded against loops).
Every chunk is precached, so the installed app opens and navigates fully offline.
Always bump the patch of APP_VERSION in every PR (it is shown in the header as V…).
src/index.css (primary = Messier green, success = teal,
warning, info, destructive), light and dark. src/lib/theme-contrast.test.js fails
the build if any token drops below WCAG contrast (4.5:1 text, 3:1 field borders).
Use the tokens instead of Tailwind palette colours (text-success, not text-green-600).SparkLog must be usable by managers at any hour. On the Supabase free tier two layers keep it responsive despite cold starts:
withRetry (src/lib/utils.js) retries a stalled read a few
times with backoff before surfacing an error, so a cold start self-heals..github/workflows/keep-warm.yml pings the public.ping() RPC
every ~10 minutes so Postgres never goes cold. Requires repo secrets SUPABASE_URL
and SUPABASE_ANON_KEY, and runs only from the default branch.This is best-effort, not an SLA. When uptime becomes critical, move to Supabase Pro
(no pausing, steadier compute, daily backups). See docs/ops/supabase-reliability.md.
A deterministic, pure source-deductions (DAS) engine lives under src/payroll/ and is
exercised from the Payroll → Calculation bench. It is a test bench only — every
result is labeled "Not finalized payroll · Requires payroll review" and the rule set
stays draft (requires_review = true) until validated against WebRAS/PDOC with
specialist sign-off (per the payroll-rule gate). Money is computed in integer cents.
src/payroll/ccq-benefits.js) — folds collective-agreement items into
the right bases before the tax engine (électricien, annexe C3): 13 % vacation
indemnity (pensionable/insurable), the MÉDIC avantage imposable (3,377 $/h — CCQ
Avantages imposables table; Québec-taxable + pensionable but excluded from the federal
source base → T4A), the employee pension contribution (wage × 1,13 × 9 %, deductible),
the MÉDIC premium withholding, and union dues auto-computed per union
(CCQ_UNIONS: FTQ-FIPOE, International/FIPOE 568, CSD, CSN, SQC) as a federal U1
deduction / Québec credit.docs/rules/2026-das-payroll.md.Hours classification is in production. The dollar amounts stay an estimate until:
docs/rules/compensation-rules.md); still
open: which wages the CNESST premium applies to (the "admissible" wage), union locals.RULE_VERSION.status becomes
validated.The itemised, up-to-date list is in CLAUDE.md §6.1.
Payroll-adjacent work follows a scope-and-rule discipline (see GPT.md and the docs/
tree):
docs/adr/ — product-scope decision: SparkLog is a timekeeping + payroll-export
tool, not a payroll engine. Every calculated value stays labeled as an estimate /
preview until a qualified specialist validates it.docs/rules/ — approved compensation rules with their CCQ sources (system of record:
the payroll_rules table).docs/governance/OWNERSHIP.md — named owners and approval gates.docs/security/authorization-matrix.md — the enforced RLS matrix, plus the runnable
adversarial suite at tests/rls/authorization_matrix.sql.docs/inventory/ — every calculation and export surface..github/pull_request_template.md — a required payroll-rule gate: no new pay rule
merges without a cited source and specialist approval.VITE_* variables as public.employee_sensitive keeps only the secret id and has no table policy for the browser. Only the owner-only RPCs reveal_nas / set_nas / nas_presence / export_ccq_nas reach it, each audited; set_nas rejects a bad check digit. Other managers and admin staff only see a mask. Wage information is sensitive; restrict access and production logs accordingly.get_my_role() returns manager and is_privileged() is true only for an aal2 session, so a stolen password alone opens neither Gestion nor NAS data.Private / Internal Use — All rights reserved.
Before the first monthly report is sent to the CCQ, every value that feeds the export must be confirmed — the app can warn when a field is empty, but it cannot know whether a filled-in value is correct. Work through this list with the person responsible for payroll/CCQ (the "approver" column).
The CCQ monthly report is due by the 15th of the month following the work period, so this verification can happen after the crew starts recording hours — it does not block time-sheet go-live.
Legend: ✓ = confirmed correct · ✗ = needs fixing · note the correction.
Confirmed in Manager → Employees. Source of truth is the employee's CCQ certificat de compétence.
| Field | App field | Where it appears on the report | ✓ / ✗ | Correction / notes |
|---|---|---|---|---|
| Full name | full_name | Identification (D) — any error rejects the line | ||
| Social insurance number (NAS) | nas_employee | Identification (D) | ||
| Work region | work_region | Région de travail (K) — confirm against current CCQ region list | ||
| Appendix / annexe | wage_schedule | Annexe/Salaire (J), exported as C-3 | ||
| Union association | union_association | Union/syndicat (L) — FTQ/International also need the local number | ||
| Hourly rate | hourly_rate | Taux horaire — must match the confirmed annexe | ||
| Apprenticeship level | apprentice_level | Période d'apprentissage (F) |
| Assumption | Current value | Confirm |
|---|---|---|
| Occupation / trade | Électricien 220 | |
| Sector | Institutionnel & commercial → C | |
| Week ending | Saturday | |
| Everyone reported as regular salaried worker (blank status) | yes | |
| Statutory holidays / construction vacations | handled — non-working (entry blocked via company_holidays); 13 % leave indemnity computed |
Wage and benefit rates are fetched from the CCQ rate snapshot; the flat contributions and the union-specific dues are not yet computed by the app (planned for the remittance stage) and must be verified against the current official tables before any dollar figure is trusted.
| Rate | Source | Confirm current value |
|---|---|---|
| Regular / 150% / 200% hourly rates | CCQ rate sync (ccq_rate_snapshots) | |
| Avantages sociaux ($/h) | CCQ rate sync | |
| Congés & jours fériés (13 % = 6 % vac + 5.5 % fériés + 1.5 % maladie) | CCQ chèque-vacances — confirmed | |
| Prélèvement CCQ: 0.75 % employee share + 0.75 % employer share (ACQ grid, 26 Apr 2026, note 10) — team-leader premium in/out of the base still to confirm | CCQ | |
| Contribution sectorielle ($0.02/h) | CCQ | |
| Fonds d'indemnisation ($0.02/h) | CCQ | |
| Fonds de formation ($0.20/h) | CCQ | |
| AECQ ($0.03/h + $225 annual, October) | AECQ | |
| TPS / TVQ | Federal / Québec (TVQ is 9.975 %) | |
| Union dues — per union / local | Table des taux de cotisation syndicale |
Dues and, in some cases, benefit handling differ by union (CSD, CSN, SQC, FTQ‑Construction, International/CPQMCI). List each union present in the crew and its confirmed dues rate (and local, for FTQ/International):
| Union | Local (if applicable) | Dues rate | Confirmed by |
|---|---|---|---|
Approved by: _________________ Date: _________
This export is an integration-ready internal format, not a certified CCQ submission file. Validate against the CCQ's current requirements before use in production payroll.
#/manager/inventory/minimumsemployee_groups.low_stock_alertstart_date: a date still to come keeps the
person « Waiting to start ») and the last day worked (last_work_date, new; never before the hiring date; each
change in the Journal). The Record of employment uses these two dates when they are filled. The CCQ section is now
called « CCQ data ».src/components/employee-form/, src/components/employees/), with screen tests (jsdom + Testing Library,
src/test/fake-supabase.js) written first and passing on both versions. ESLint goes from 64 warnings to 0 and
npm run lint now fails on any warning. Fixed on the way: switching the payroll method (Messier / Reference) now
recomputes the DAS screen; the service-health card no longer retries a failed load in a loop.?tab= address and the access rules are unchanged. Pages with 3 tabs or fewer keep horizontal tabs.#/manager/timesheets,
payroll/das, config/rules…) opens the page and tab that hold it now, with its filters (employee, day, method…).?view=month; former ?tab=week / ?tab=month links still open it). A line under each
report says what it shows: Costs = what each employee costs (pay, employer charges, km, expenses) for a week, the
month or the year; Week / Month = the whole team's totals per CCQ week or monthly CCQ period; Summary = each
employee's hours for one CCQ week, day by day, with the printable weekly summary.America/Toronto. The full configured deadline minute is accepted (the default 23:59 blocks at midnight); CCQ construction vacations and statutory holidays are installed automatically by dated migrations, and managers can unlock a specific employee/date.