Expose ShiftKind in admin panel with Dropdown.Button picker
Shift.kind existed in the schema and on the volunteer dashboard's training filter but there was no way to create or edit a shift's kind from the admin UI — every shift landed as the default CANVASS. This wires the full loop: Backend: createShiftSchema / updateShiftSchema / listShiftsSchema and their series counterparts now accept a kind field. The shifts service passes it through on create and filters by it on list. Series shift templates propagate kind to every generated shift instance so a training series produces training shifts. Admin UI: the Create Shift button becomes a Dropdown.Button. The main action creates a Canvass shift (default); the menu offers Training, Event Staffing, Phone Bank, and Other. Each menu item pre-fills the form's kind field. A kind Select appears at the top of the form so admins can change it mid-creation or on edit. The shifts table gets a color-coded Kind column and the toolbar gets a kind filter. Bunker Admin
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
import { RecurrenceFrequency } from '@prisma/client';
|
||||
import { RecurrenceFrequency, ShiftKind } from '@prisma/client';
|
||||
|
||||
export const createShiftSeriesSchema = z.object({
|
||||
title: z.string().min(1, 'Title is required'),
|
||||
@@ -10,6 +10,7 @@ export const createShiftSeriesSchema = z.object({
|
||||
maxVolunteers: z.number().int().min(1, 'Must have at least 1 volunteer spot'),
|
||||
isPublic: z.boolean().optional().default(false),
|
||||
cutId: z.string().optional(),
|
||||
kind: z.nativeEnum(ShiftKind).optional().default(ShiftKind.CANVASS),
|
||||
|
||||
// Recurrence
|
||||
frequency: z.nativeEnum(RecurrenceFrequency),
|
||||
|
||||
@@ -101,6 +101,7 @@ export class ShiftSeriesService {
|
||||
maxVolunteers: input.maxVolunteers,
|
||||
isPublic: input.isPublic ?? false,
|
||||
cutId: input.cutId,
|
||||
kind: input.kind,
|
||||
seriesId: series.id,
|
||||
createdBy,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
import { ShiftStatus } from '@prisma/client';
|
||||
import { ShiftStatus, ShiftKind } from '@prisma/client';
|
||||
|
||||
export const createShiftSchema = z.object({
|
||||
title: z.string().min(1, 'Title is required'),
|
||||
@@ -11,6 +11,7 @@ export const createShiftSchema = z.object({
|
||||
maxVolunteers: z.number().int().min(1, 'Must have at least 1 volunteer spot'),
|
||||
isPublic: z.boolean().optional().default(false),
|
||||
cutId: z.string().optional(),
|
||||
kind: z.nativeEnum(ShiftKind).optional().default(ShiftKind.CANVASS),
|
||||
});
|
||||
|
||||
export const updateShiftSchema = z.object({
|
||||
@@ -24,6 +25,7 @@ export const updateShiftSchema = z.object({
|
||||
isPublic: z.boolean().optional(),
|
||||
status: z.nativeEnum(ShiftStatus).optional(),
|
||||
cutId: z.string().nullable().optional(),
|
||||
kind: z.nativeEnum(ShiftKind).optional(),
|
||||
});
|
||||
|
||||
export const listShiftsSchema = z.object({
|
||||
@@ -31,6 +33,7 @@ export const listShiftsSchema = z.object({
|
||||
limit: z.coerce.number().int().positive().max(100).default(20),
|
||||
search: z.string().optional(),
|
||||
status: z.nativeEnum(ShiftStatus).optional(),
|
||||
kind: z.nativeEnum(ShiftKind).optional(),
|
||||
upcoming: z.coerce.boolean().optional(),
|
||||
sortBy: z.enum(['date', 'createdAt', 'title']).optional().default('date'),
|
||||
sortOrder: z.enum(['asc', 'desc']).optional().default('desc'),
|
||||
|
||||
@@ -39,7 +39,7 @@ const meetingSelect = {
|
||||
|
||||
export const shiftsService = {
|
||||
async findAll(filters: ListShiftsInput) {
|
||||
const { page, limit, search, status, upcoming, sortBy, sortOrder } = filters;
|
||||
const { page, limit, search, status, kind, upcoming, sortBy, sortOrder } = filters;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const where: Prisma.ShiftWhereInput = {};
|
||||
@@ -52,6 +52,7 @@ export const shiftsService = {
|
||||
}
|
||||
|
||||
if (status) where.status = status;
|
||||
if (kind) where.kind = kind;
|
||||
|
||||
if (upcoming) {
|
||||
where.date = { gte: new Date() };
|
||||
@@ -132,6 +133,7 @@ export const shiftsService = {
|
||||
maxVolunteers: data.maxVolunteers,
|
||||
isPublic: data.isPublic,
|
||||
cutId: data.cutId,
|
||||
kind: data.kind,
|
||||
createdBy: userId,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user