feat: complete ShiftCraft — AI-powered shift scheduling SaaS
Complete implementation including:
- Landing page with hero, features, how-it-works, pricing
- Employee management (CRUD with soft delete)
- AI constraint parser (Anthropic Claude API)
- German labor law templates (ArbZG §3, §5, §9)
- HiGHS ILP solver for optimal fair schedules
- Schedule calendar result view (employee × date grid)
- Shift framework configuration (periods + shifts)
- Subscription tiers: Free / Pro / Business
- PocketBase setup script with collection creation + seed data
- .env.example with all required variables documented
Pages: employees, constraints (list/new/templates), schedules (list/new/[id]),
settings (organization/shifts/billing), dashboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
31
app/utils/constraintDisplay.ts
Normal file
31
app/utils/constraintDisplay.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { ConstraintJSON } from '~/shared/types/constraint'
|
||||
|
||||
export function constraintToHuman(constraint: ConstraintJSON): string {
|
||||
return constraint.natural_language_summary || formatConstraintFallback(constraint)
|
||||
}
|
||||
|
||||
function formatConstraintFallback(c: ConstraintJSON): string {
|
||||
const params = c.params as Record<string, unknown>
|
||||
switch (c.type) {
|
||||
case 'max_hours_per_day':
|
||||
return `Maximal ${params.max_hours} Stunden pro Tag`
|
||||
case 'max_hours_per_week':
|
||||
return `Maximal ${params.max_hours} Stunden pro Woche`
|
||||
case 'min_rest_between_shifts':
|
||||
return `Mindestens ${params.min_hours} Stunden Ruhezeit zwischen Schichten`
|
||||
case 'max_consecutive_shifts':
|
||||
return `Maximal ${params.max_count} Schichten am Stück`
|
||||
case 'max_consecutive_shift_type':
|
||||
return `Maximal ${params.max_count} ${params.period_id}-Schichten am Stück`
|
||||
case 'forbidden_shift_sequence':
|
||||
return `${params.first_period_id}-Schicht darf nicht direkt auf ${params.second_period_id}-Schicht folgen`
|
||||
case 'employee_avoids_period':
|
||||
return `Mitarbeiter bevorzugt keine ${params.period_id}-Schichten`
|
||||
case 'employee_prefers_period':
|
||||
return `Mitarbeiter bevorzugt ${params.period_id}-Schichten`
|
||||
case 'fair_distribution':
|
||||
return `Faire Verteilung der ${params.metric === 'night_shifts' ? 'Nachtschichten' : 'Schichten'} (max. ${params.max_deviation_percent}% Abweichung)`
|
||||
default:
|
||||
return c.type.replace(/_/g, ' ')
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user