From 359d38a3b6cd3a5ae56129cbafa63f6b27cf00dd Mon Sep 17 00:00:00 2001 From: Soubzar Date: Tue, 26 May 2026 13:57:48 +0200 Subject: [PATCH 01/11] Add phone number to presets - v1: all files modified manually --- .gitignore | 4 ++- Dockerfile | 12 +++++++++ app/api/external-syncs/[id]/sync/route.ts | 8 ++++++ app/api/presets/[id]/route.ts | 3 +++ app/api/presets/route.ts | 2 ++ app/api/shifts/[id]/route.ts | 3 +++ app/api/shifts/route.ts | 4 +++ app/api/shifts/stats/route.ts | 2 ++ app/page.tsx | 1 + components/preset-manage-sheet.tsx | 30 ++++++++++++++++++++++- components/shift-card.tsx | 4 +++ components/shift-form-fields.tsx | 23 +++++++++++++++++ components/shift-sheet.tsx | 3 +++ docker-compose.yml | 9 ++++--- hooks/usePresets.ts | 2 ++ hooks/useShiftActions.ts | 4 ++- hooks/useShiftForm.ts | 6 +++++ hooks/useShifts.ts | 2 ++ lib/db/schema.ts | 2 ++ lib/external-calendar-utils.ts | 7 ++++++ lib/types.ts | 1 + messages/en.json | 2 ++ 22 files changed, 127 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index a9fe888..ed4c3fd 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,8 @@ # testing /coverage +*.cer + # next.js /.next/ /out/ @@ -79,4 +81,4 @@ public/workbox-*.js public/workbox-*.js.map # Local scripts -.LOCAL \ No newline at end of file +.LOCAL diff --git a/Dockerfile b/Dockerfile index 1981eb9..f0ae27d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,10 @@ # Stage 1: Dependencies FROM node:20-alpine AS deps +COPY Fortinet_CA_SSL.cer /usr/local/share/ca-certificates/Fortinet_CA_SSL.crt +RUN cat /usr/local/share/ca-certificates/Fortinet_CA_SSL.crt >> /etc/ssl/certs/ca-certificates.crt RUN apk add --no-cache libc6-compat +ENV NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/Fortinet_CA_SSL.crt WORKDIR /app # Copy only package files for better caching @@ -12,7 +15,10 @@ RUN --mount=type=cache,target=/root/.npm-deps \ # Stage 2: Builder FROM node:20-alpine AS builder +COPY Fortinet_CA_SSL.cer /usr/local/share/ca-certificates/Fortinet_CA_SSL.crt +RUN cat /usr/local/share/ca-certificates/Fortinet_CA_SSL.crt >> /etc/ssl/certs/ca-certificates.crt RUN apk add --no-cache libc6-compat +ENV NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/Fortinet_CA_SSL.crt WORKDIR /app # Build argument for version @@ -34,7 +40,10 @@ RUN --mount=type=cache,target=/app/.next/cache \ # Stage 3: Production dependencies FROM node:20-alpine AS prod-deps +COPY Fortinet_CA_SSL.cer /usr/local/share/ca-certificates/Fortinet_CA_SSL.crt +RUN cat /usr/local/share/ca-certificates/Fortinet_CA_SSL.crt >> /etc/ssl/certs/ca-certificates.crt RUN apk add --no-cache libc6-compat +ENV NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/Fortinet_CA_SSL.crt WORKDIR /app # Copy package files @@ -47,7 +56,10 @@ RUN --mount=type=cache,target=/root/.npm-prod \ # Stage 4: Runner FROM node:20-alpine AS runner +COPY Fortinet_CA_SSL.cer /usr/local/share/ca-certificates/Fortinet_CA_SSL.crt +RUN cat /usr/local/share/ca-certificates/Fortinet_CA_SSL.crt >> /etc/ssl/certs/ca-certificates.crt RUN apk add --no-cache libc6-compat dumb-init +ENV NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/Fortinet_CA_SSL.crt WORKDIR /app # Build-time metadata (passed from GitHub Actions) diff --git a/app/api/external-syncs/[id]/sync/route.ts b/app/api/external-syncs/[id]/sync/route.ts index b09f0f6..1455023 100644 --- a/app/api/external-syncs/[id]/sync/route.ts +++ b/app/api/external-syncs/[id]/sync/route.ts @@ -117,11 +117,13 @@ export async function syncExternalCalendar( string, typeof shifts.$inferSelect >(); + for (const shift of existingShifts) { const fingerprint = createEventFingerprint( shift.date, shift.startTime, shift.endTime, + shift.number, shift.title, undefined, // Only use externalEventId for iCloud/Google (stable UIDs) @@ -175,6 +177,7 @@ export async function syncExternalCalendar( : baseEventId; const title = event.summary || "Untitled Event"; + const number = event.organizer || ""; // Create fingerprint based on event content // For iCloud/Google: include eventId for stable UID-based matching @@ -184,6 +187,7 @@ export async function syncExternalCalendar( dayEntry.startTime, dayEntry.endTime, title, + number, undefined, externalSync.syncType !== "custom" ? eventId : undefined ); @@ -196,6 +200,7 @@ export async function syncExternalCalendar( startTime: dayEntry.startTime, endTime: dayEntry.endTime, title, + number: "", color: externalSync.color, notes: event.description || null, isAllDay, @@ -255,6 +260,7 @@ export async function syncExternalCalendar( todoData.date, todoData.startTime, todoData.endTime, + todoData.number, title, undefined, externalSync.syncType !== "custom" ? eventId : undefined @@ -268,6 +274,7 @@ export async function syncExternalCalendar( startTime: todoData.startTime, endTime: todoData.endTime, title, + number: todoData.number, color: externalSync.color, notes: todoData.notes, isAllDay: todoData.isAllDay, @@ -310,6 +317,7 @@ export async function syncExternalCalendar( shift.date, shift.startTime, shift.endTime, + shift.number, shift.title, undefined, // Only use externalEventId for iCloud/Google (stable UIDs) diff --git a/app/api/presets/[id]/route.ts b/app/api/presets/[id]/route.ts index 5cb6642..b130db4 100644 --- a/app/api/presets/[id]/route.ts +++ b/app/api/presets/[id]/route.ts @@ -67,6 +67,7 @@ export async function PATCH( title, startTime, endTime, + number, color, notes, isSecondary, @@ -117,6 +118,7 @@ export async function PATCH( title, startTime: isAllDay ? "00:00" : startTime, endTime: isAllDay ? "23:59" : endTime, + number: number !== undefined ? number : "", color, notes: notes || null, isSecondary: isSecondary !== undefined ? isSecondary : undefined, @@ -136,6 +138,7 @@ export async function PATCH( endTime: isAllDay ? "23:59" : endTime, color, notes: notes || null, + number: number !== undefined ? number : "", isAllDay: isAllDay !== undefined ? isAllDay : undefined, updatedAt: new Date(), }) diff --git a/app/api/presets/route.ts b/app/api/presets/route.ts index 5c13ccc..762af5a 100644 --- a/app/api/presets/route.ts +++ b/app/api/presets/route.ts @@ -66,6 +66,7 @@ export async function POST(request: NextRequest) { title, startTime, endTime, + number, color, notes, isSecondary, @@ -122,6 +123,7 @@ export async function POST(request: NextRequest) { title, startTime: isAllDay ? "00:00" : startTime, endTime: isAllDay ? "23:59" : endTime, + number, color: color || "#3b82f6", notes: notes || null, isSecondary: isSecondary || false, diff --git a/app/api/shifts/[id]/route.ts b/app/api/shifts/[id]/route.ts index cdc07a2..4e9a780 100644 --- a/app/api/shifts/[id]/route.ts +++ b/app/api/shifts/[id]/route.ts @@ -4,6 +4,7 @@ import { calendars, shifts } from "@/lib/db/schema"; import { eq } from "drizzle-orm"; import { getSessionUser } from "@/lib/auth/sessions"; import { canViewCalendar, canEditCalendar } from "@/lib/auth/permissions"; +import { number } from "better-auth"; // GET single shift export async function GET( @@ -21,6 +22,7 @@ export async function GET( date: shifts.date, startTime: shifts.startTime, endTime: shifts.endTime, + number: shifts.number, title: shifts.title, color: shifts.color, notes: shifts.notes, @@ -139,6 +141,7 @@ export async function PUT( date: body.date ? new Date(body.date) : existingShift.date, startTime: body.startTime ?? existingShift.startTime, endTime: body.endTime ?? existingShift.endTime, + number: body.number ?? existingShift.number, title: body.title ?? existingShift.title, color: body.color ?? existingShift.color, notes: body.notes ?? existingShift.notes, diff --git a/app/api/shifts/route.ts b/app/api/shifts/route.ts index 4292fd0..a9f1926 100644 --- a/app/api/shifts/route.ts +++ b/app/api/shifts/route.ts @@ -5,6 +5,7 @@ import { eq, and, gte, lte, or, isNull } from "drizzle-orm"; import { getSessionUser } from "@/lib/auth/sessions"; import { canViewCalendar, canEditCalendar } from "@/lib/auth/permissions"; import { parseLocalDate } from "@/lib/date-utils"; +import { number } from "better-auth"; // GET shifts for a calendar (with optional date filter) export async function GET(request: Request) { @@ -51,6 +52,7 @@ export async function GET(request: Request) { date: shifts.date, startTime: shifts.startTime, endTime: shifts.endTime, + number: shifts.number, title: shifts.title, color: shifts.color, notes: shifts.notes, @@ -124,6 +126,7 @@ export async function POST(request: Request) { startTime, endTime, title, + number, color, notes, presetId, @@ -181,6 +184,7 @@ export async function POST(request: Request) { startTime: isAllDay ? "00:00" : startTime, endTime: isAllDay ? "23:59" : endTime, title, + number: "", color: color || "#3b82f6", notes: notes || null, isAllDay: isAllDay || false, diff --git a/app/api/shifts/stats/route.ts b/app/api/shifts/stats/route.ts index a06d0f3..0e8c0ae 100644 --- a/app/api/shifts/stats/route.ts +++ b/app/api/shifts/stats/route.ts @@ -18,6 +18,7 @@ import { startOfYear, endOfYear, } from "date-fns"; +import { number } from "better-auth"; // GET shift statistics for a calendar export async function GET(request: Request) { @@ -88,6 +89,7 @@ export async function GET(request: Request) { date: shifts.date, startTime: shifts.startTime, endTime: shifts.endTime, + number: shifts.number, isAllDay: shifts.isAllDay, }) .from(shifts) diff --git a/app/page.tsx b/app/page.tsx index 6c57879..7da9bef 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -492,6 +492,7 @@ function HomeContent() { startTime: preset.startTime, endTime: preset.endTime, title: preset.title, + number: preset.number, color: preset.color, notes: preset.notes || "", presetId: preset.id, diff --git a/components/preset-manage-sheet.tsx b/components/preset-manage-sheet.tsx index 9411285..666735b 100644 --- a/components/preset-manage-sheet.tsx +++ b/components/preset-manage-sheet.tsx @@ -46,6 +46,7 @@ interface PresetFormData { title: string; startTime: string; endTime: string; + number: string; color: string; notes: string; isSecondary: boolean; @@ -198,6 +199,7 @@ export function PresetManageSheet({ title: "", startTime: "09:00", endTime: "17:00", + number: "", color: PRESET_COLORS[0].value, notes: "", isSecondary: false, @@ -306,6 +308,7 @@ export function PresetManageSheet({ title: "", startTime: "09:00", endTime: "17:00", + number: "", color: PRESET_COLORS[0].value, notes: "", isSecondary: false, @@ -337,6 +340,7 @@ export function PresetManageSheet({ title: "", startTime: "09:00", endTime: "17:00", + number: "", color: PRESET_COLORS[0].value, notes: "", isSecondary: false, @@ -379,6 +383,7 @@ export function PresetManageSheet({ title: preset.title, startTime: preset.startTime, endTime: preset.endTime, + number: preset.number, color: preset.color, notes: preset.notes || "", isSecondary: preset.isSecondary || false, @@ -398,6 +403,7 @@ export function PresetManageSheet({ title: "", startTime: "09:00", endTime: "17:00", + number: "", color: PRESET_COLORS[0].value, notes: "", isSecondary: false, @@ -419,6 +425,7 @@ export function PresetManageSheet({ formData.title.trim() !== "" || formData.startTime !== "09:00" || formData.endTime !== "17:00" || + formData.number.trim() !== "" || formData.color !== PRESET_COLORS[0].value || formData.notes.trim() !== "" || formData.isSecondary || @@ -438,6 +445,7 @@ export function PresetManageSheet({ title: "", startTime: "09:00", endTime: "17:00", + number: "", color: PRESET_COLORS[0].value, notes: "", isSecondary: false, @@ -466,6 +474,7 @@ export function PresetManageSheet({ title: "", startTime: "09:00", endTime: "17:00", + number: "", color: PRESET_COLORS[0].value, notes: "", isSecondary: false, @@ -486,7 +495,7 @@ export function PresetManageSheet({ {t("preset.manage")} - {t("preset.manageDescription")} + test @@ -638,6 +647,24 @@ export function PresetManageSheet({ + + + setFormData({ ...formData, number: e.target.value }) + } + className="h-11 border-border/50 focus:border-primary/50 focus:ring-primary/20 bg-background/50" + disabled={isLoading} + /> + {!formData.isAllDay && ( {shift.endTime} )} + +

+ {shift.number} +

{shift.notes && (

diff --git a/components/shift-form-fields.tsx b/components/shift-form-fields.tsx index 2d3dcf8..611ae10 100644 --- a/components/shift-form-fields.tsx +++ b/components/shift-form-fields.tsx @@ -137,6 +137,29 @@ export function ShiftFormFields({ /> +

+ + + onFormDataChange({ ...formData, number: e.target.value }) + } + onBlur={onBlur} + disabled={readOnly} + className="h-11 border-border/50 focus:border-primary/50 focus:ring-primary/20 bg-background/50 backdrop-blur-sm" + autoFocus={!readOnly} + /> +
+ +