This commit is contained in:
Stefan Lazic committed 2026-08-03 21:15:24 +01:00
1 parent ec5583f972
commit 546613e677
189 files changed
+9873 -655

No files matched your search

@@ -12,6 +12,7 @@ import {
start,
} from "@/services/scheduling/appointment.service";
import { awardStampForCompletedAppointment } from "@/services/loyalty/loyalty.service";
import { notifyAppointmentNoShow, notifyBookingApproved, notifyBookingCompleted } from "@/services/scheduling/booking-notifications";
import { assertOwnAppointment, StaffPermissionDeniedError } from "../authorization";
/**
@@ -58,9 +59,11 @@ export async function confirmAppointmentAction(
if (!appointment) return { success: false, error: error ?? "Not found." };
const result = await confirm(prisma, appointmentId);
return result.success
? { success: true }
: { success: false, error: friendlyTransitionMessage(result.validation.issues[0]?.code) };
if (!result.success) {
return { success: false, error: friendlyTransitionMessage(result.validation.issues[0]?.code) };
}
await notifyBookingApproved(prisma, result.appointment);
return { success: true };
}
export async function checkInAppointmentAction(
@@ -97,6 +100,7 @@ export async function completeAppointmentAction(
if (!result.success) {
return { success: false, error: friendlyTransitionMessage(result.validation.issues[0]?.code) };
}
await notifyBookingCompleted(prisma, result.appointment);
const loyaltyResult = await awardStampForCompletedAppointment(prisma, {
appointmentId,
@@ -115,9 +119,11 @@ export async function markNoShowAction(
if (!appointment) return { success: false, error: error ?? "Not found." };
const result = await markNoShow(prisma, appointmentId);
return result.success
? { success: true }
: { success: false, error: friendlyTransitionMessage(result.validation.issues[0]?.code) };
if (!result.success) {
return { success: false, error: friendlyTransitionMessage(result.validation.issues[0]?.code) };
}
await notifyAppointmentNoShow(prisma, result.appointment);
return { success: true };
}
export async function cancelStaffAppointmentAction(params: {
+13 -4
View File
@@ -1,9 +1,11 @@
"use server";
import { NotificationType } from "@/generated/prisma/enums";
import { prisma } from "@/lib/db/prisma";
import { getActingStaff } from "@/lib/staff/get-acting-staff";
import { setNotificationPreferences, updateStaffProfile } from "@/services/staff/staff.service";
import type { NotificationPreferenceCell } from "@/services/staff/staff.service";
import { setNotificationPreference } from "@/services/notifications/notification.service";
import type { PreferenceCellState } from "@/features/staff/components/notification-preferences-form";
import { updateStaffProfile } from "@/services/staff/staff.service";
export interface ProfileActionResult {
success: boolean;
@@ -35,11 +37,18 @@ export async function updateProfileAction(input: {
}
export async function saveNotificationPreferencesAction(
cells: NotificationPreferenceCell[],
cells: PreferenceCellState[],
): Promise<ProfileActionResult> {
const staff = await getActingStaff();
if (!staff) return { success: false, error: "You must be signed in as staff." };
await setNotificationPreferences(prisma, staff.userId, cells);
for (const cell of cells) {
await setNotificationPreference(prisma, {
userId: staff.userId,
type: NotificationType.STAFF_APPOINTMENT_REMINDER,
channel: cell.channel,
isEnabled: cell.isEnabled,
});
}
return { success: true };
}
+1 -1
View File
@@ -174,7 +174,7 @@ export async function confirmStaffBookingAction(notes?: string): Promise<StaffBo
return { success: false, error: getFirstFriendlyMessage(result.validation) };
}
await writeLastBookingReference(result.appointment.id);
await writeLastBookingReference({ appointmentId: result.appointment.id });
await clearStaffDraft();
redirect(`/staff/appointments/${result.appointment.id}`);
}
+28 -3
View File
@@ -5,18 +5,43 @@ import { getActingStaff } from "@/lib/staff/get-acting-staff";
import { setWorkingHours } from "@/services/staff/staff.service";
import type { WorkingHoursDayInput } from "@/services/staff/staff.service";
export interface WorkingHoursConflictItem {
id: string;
customerName: string;
serviceNames: string;
startAt: string;
}
export interface WorkingHoursActionResult {
success: boolean;
error?: string;
conflicts?: WorkingHoursConflictItem[];
}
export async function saveWorkingHoursAction(days: WorkingHoursDayInput[]): Promise<WorkingHoursActionResult> {
export async function saveWorkingHoursAction(
days: WorkingHoursDayInput[],
acknowledgeConflicts = false,
): Promise<WorkingHoursActionResult> {
const staff = await getActingStaff();
if (!staff) return { success: false, error: "You must be signed in as staff." };
const result = await setWorkingHours(prisma, { staffId: staff.id, days });
const result = await setWorkingHours(prisma, {
staffId: staff.id,
businessTimezone: staff.business.timezone,
days,
acknowledgeConflicts,
});
if (!result.success) {
return { success: false, error: result.validation.issues[0]?.message ?? "Couldn't save working hours." };
return {
success: false,
error: result.validation.issues[0]?.message ?? "Couldn't save working hours.",
conflicts: result.conflicts.map((c) => ({
id: c.id,
customerName: `${c.customer.firstName} ${c.customer.lastName}`,
serviceNames: c.services.map((s) => s.service.name).join(" + "),
startAt: c.startAt.toISOString(),
})),
};
}
return { success: true };
}