Fix calendar style

This commit is contained in:
2026-06-14 13:05:53 +02:00
parent 4924982a8a
commit 6ed2a3ae0c
10 changed files with 1005 additions and 31 deletions
@@ -0,0 +1,197 @@
import { ReactNode, useState } from 'react'
import './manual-fetch-modal.css'
export interface ManualFetchModalProps {
isOpen: boolean
onSave: (data: {
totalDays: number
daysUsed: number
reservedDaysLeft: number
transferableDaysLeft: number
dubbeldagarLeft: number
sjukpenningUsed: number
lagstaNivaUsed: number
}) => Promise<void>
onClose: () => void
isSaving?: boolean
error?: string | null
}
export function ManualFetchModal({
isOpen,
onSave,
onClose,
isSaving = false,
error = null,
}: ManualFetchModalProps): ReactNode {
const [totalDays, setTotalDays] = useState(480)
const [daysUsed, setDaysUsed] = useState(0)
const [reservedDaysLeft, setReservedDaysLeft] = useState(90)
const [transferableDaysLeft, setTransferableDaysLeft] = useState(0)
const [dubbeldagarLeft, setDubbeldagarLeft] = useState(60)
const [sjukpenningUsed, setSjukpenningUsed] = useState(0)
const [lagstaNivaUsed, setLagstaNivaUsed] = useState(0)
const [localError, setLocalError] = useState<string | null>(null)
const handleSave = async () => {
try {
setLocalError(null)
await onSave({
totalDays,
daysUsed,
reservedDaysLeft,
transferableDaysLeft,
dubbeldagarLeft,
sjukpenningUsed,
lagstaNivaUsed,
})
} catch (err) {
setLocalError(err instanceof Error ? err.message : 'Failed to save manual data')
}
}
if (!isOpen) return null
const daysLeft = totalDays - daysUsed
return (
<div className="manual-fetch-modal__overlay" onClick={onClose}>
<div className="manual-fetch-modal__content" onClick={(e) => e.stopPropagation()}>
<div className="manual-fetch-modal__header">
<h2>Enter Parental Leave Data</h2>
<button className="manual-fetch-modal__close" onClick={onClose} aria-label="Close">
×
</button>
</div>
<div className="manual-fetch-modal__body">
<p className="manual-fetch-modal__description">
Enter your parental leave information from Försäkringskassan manually if automatic fetching is unavailable.
</p>
{(error || localError) && (
<div className="manual-fetch-modal__error" role="alert">
{error || localError}
</div>
)}
<div className="manual-fetch-modal__section">
<h3>Total Days</h3>
<div className="manual-fetch-modal__field">
<label htmlFor="total-days">Total days (0-480)</label>
<input
id="total-days"
type="number"
min="0"
max="480"
value={totalDays}
onChange={(e) => setTotalDays(Number(e.target.value))}
disabled={isSaving}
/>
</div>
<div className="manual-fetch-modal__field">
<label htmlFor="days-used">Days used</label>
<input
id="days-used"
type="number"
min="0"
value={daysUsed}
onChange={(e) => setDaysUsed(Number(e.target.value))}
disabled={isSaving}
/>
</div>
<div className="manual-fetch-modal__info">
Days remaining: <strong>{daysLeft}</strong>
</div>
</div>
<div className="manual-fetch-modal__section">
<h3>Remaining Days Breakdown</h3>
<div className="manual-fetch-modal__field">
<label htmlFor="reserved-days">Reserved days left</label>
<input
id="reserved-days"
type="number"
min="0"
value={reservedDaysLeft}
onChange={(e) => setReservedDaysLeft(Number(e.target.value))}
disabled={isSaving}
/>
</div>
<div className="manual-fetch-modal__field">
<label htmlFor="transferable-days">Transferable days left</label>
<input
id="transferable-days"
type="number"
min="0"
value={transferableDaysLeft}
onChange={(e) => setTransferableDaysLeft(Number(e.target.value))}
disabled={isSaving}
/>
</div>
<div className="manual-fetch-modal__field">
<label htmlFor="dubbeldag-left">Dubbeldagar left (0-60)</label>
<input
id="dubbeldag-left"
type="number"
min="0"
max="60"
value={dubbeldagarLeft}
onChange={(e) => setDubbeldagarLeft(Number(e.target.value))}
disabled={isSaving}
/>
</div>
</div>
<div className="manual-fetch-modal__section">
<h3>Tier Breakdown</h3>
<div className="manual-fetch-modal__field">
<label htmlFor="sjukpenning-used">Sick leave (390 level) used</label>
<input
id="sjukpenning-used"
type="number"
min="0"
value={sjukpenningUsed}
onChange={(e) => setSjukpenningUsed(Number(e.target.value))}
disabled={isSaving}
/>
</div>
<div className="manual-fetch-modal__field">
<label htmlFor="lagsta-niva-used">Parental (90 level) used</label>
<input
id="lagsta-niva-used"
type="number"
min="0"
value={lagstaNivaUsed}
onChange={(e) => setLagstaNivaUsed(Number(e.target.value))}
disabled={isSaving}
/>
</div>
</div>
</div>
<div className="manual-fetch-modal__footer">
<button
className="manual-fetch-modal__button manual-fetch-modal__button--secondary"
onClick={onClose}
disabled={isSaving}
>
Cancel
</button>
<button
className="manual-fetch-modal__button manual-fetch-modal__button--primary"
onClick={handleSave}
disabled={isSaving}
>
{isSaving ? 'Saving...' : 'Save'}
</button>
</div>
</div>
</div>
)
}
@@ -0,0 +1,190 @@
import { ReactNode, useState, useEffect } from 'react'
import type { PlannedLeaveDay } from '@/types'
import './planned-day-modal.css'
export interface PlannedDayModalProps {
date: string
isOpen: boolean
existingDay?: PlannedLeaveDay | null | undefined
parents: Array<{ id: string; name: string }>
selectedParentId: string
onSave: (payload: {
dayType: 'sjukpenning' | 'lagsta' | 'vacation'
parentId: string
isDubbeldag: boolean
}) => Promise<void>
onDelete?: (() => Promise<void>) | undefined
onClose: () => void
isSaving?: boolean | undefined
error?: string | null | undefined
}
export function PlannedDayModal({
date,
isOpen,
existingDay,
parents,
selectedParentId,
onSave,
onDelete,
onClose,
isSaving = false,
error = null,
}: PlannedDayModalProps): ReactNode {
const [dayType, setDayType] = useState<'sjukpenning' | 'lagsta' | 'vacation'>('vacation')
const [parentId, setParentId] = useState(selectedParentId)
const [isDubbeldag, setIsDubbeldag] = useState(false)
const [localError, setLocalError] = useState<string | null>(null)
useEffect(() => {
if (existingDay) {
setDayType(existingDay.dayType as 'sjukpenning' | 'lagsta' | 'vacation')
setParentId(existingDay.parentPersonalDataUuid)
setIsDubbeldag(existingDay.isDubbeldag)
} else {
setDayType('vacation')
setParentId(selectedParentId)
setIsDubbeldag(false)
}
setLocalError(null)
}, [isOpen, existingDay, selectedParentId])
const handleSave = async () => {
try {
setLocalError(null)
await onSave({
dayType,
parentId,
isDubbeldag,
})
} catch (err) {
setLocalError(err instanceof Error ? err.message : 'Failed to save planned day')
}
}
const handleDelete = async () => {
if (!onDelete) return
if (confirm('Are you sure you want to delete this planned day?')) {
try {
setLocalError(null)
await onDelete()
} catch (err) {
setLocalError(err instanceof Error ? err.message : 'Failed to delete planned day')
}
}
}
if (!isOpen) return null
const displayDate = new Date(date + 'T00:00:00Z').toLocaleDateString('sv-SE', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
})
return (
<div className="planned-day-modal__overlay" onClick={onClose}>
<div className="planned-day-modal__content" onClick={(e) => e.stopPropagation()}>
<div className="planned-day-modal__header">
<h2 data-testid="modal-title">{existingDay ? 'Edit Planned Day' : 'Plan a Day'}</h2>
<button className="planned-day-modal__close" onClick={onClose} aria-label="Close">
×
</button>
</div>
<div className="planned-day-modal__body">
<p className="planned-day-modal__date">{displayDate}</p>
{(error || localError) && (
<div className="planned-day-modal__error" role="alert">
{error || localError}
</div>
)}
<div className="planned-day-modal__field">
<label htmlFor="day-type" className="planned-day-modal__label">
Leave Type
</label>
<select
id="day-type"
value={dayType}
onChange={(e) => setDayType(e.target.value as 'sjukpenning' | 'lagsta' | 'vacation')}
className="planned-day-modal__select"
disabled={isSaving}
data-testid="day-type-select"
>
<option value="vacation">Vacation</option>
<option value="sjukpenning">Sick Leave (390 level)</option>
<option value="lagsta">Parental (90 level)</option>
</select>
</div>
{parents.length > 1 && (
<div className="planned-day-modal__field">
<label htmlFor="parent-select" className="planned-day-modal__label">
Parent
</label>
<select
id="parent-select"
value={parentId}
onChange={(e) => setParentId(e.target.value)}
className="planned-day-modal__select"
disabled={isSaving}
data-testid="parent-select"
>
{parents.map((p) => (
<option key={p.id} value={p.id}>
{p.name}
</option>
))}
</select>
</div>
)}
<div className="planned-day-modal__field">
<label className="planned-day-modal__checkbox-label">
<input
type="checkbox"
checked={isDubbeldag}
onChange={(e) => setIsDubbeldag(e.target.checked)}
disabled={isSaving}
data-testid="dubbeldag-checkbox"
/>
{' '}Double day (both parents taking leave)
</label>
</div>
</div>
<div className="planned-day-modal__footer">
{existingDay && onDelete && (
<button
className="planned-day-modal__button planned-day-modal__button--danger"
onClick={handleDelete}
disabled={isSaving}
data-testid="delete-button"
>
{isSaving ? 'Deleting...' : 'Delete'}
</button>
)}
<button
className="planned-day-modal__button planned-day-modal__button--secondary"
onClick={onClose}
disabled={isSaving}
data-testid="cancel-button"
>
Cancel
</button>
<button
className="planned-day-modal__button planned-day-modal__button--primary"
onClick={handleSave}
disabled={isSaving}
data-testid="save-button"
>
{isSaving ? 'Saving...' : 'Save'}
</button>
</div>
</div>
</div>
)
}
@@ -0,0 +1,165 @@
.manual-fetch-modal__overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.manual-fetch-modal__content {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
max-width: 600px;
width: 90%;
max-height: 85vh;
overflow-y: auto;
}
.manual-fetch-modal__header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
border-bottom: 1px solid #e0e0e0;
}
.manual-fetch-modal__header h2 {
margin: 0;
font-size: 18px;
font-weight: 600;
}
.manual-fetch-modal__close {
background: none;
border: none;
font-size: 28px;
cursor: pointer;
padding: 0;
color: #666;
transition: color 0.2s;
}
.manual-fetch-modal__close:hover {
color: #000;
}
.manual-fetch-modal__body {
padding: 20px;
}
.manual-fetch-modal__description {
font-size: 14px;
color: #666;
margin: 0 0 16px 0;
}
.manual-fetch-modal__error {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 16px;
font-size: 14px;
}
.manual-fetch-modal__section {
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 1px solid #f0f0f0;
}
.manual-fetch-modal__section:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.manual-fetch-modal__section h3 {
margin: 0 0 12px 0;
font-size: 14px;
font-weight: 600;
color: #333;
}
.manual-fetch-modal__field {
margin-bottom: 12px;
}
.manual-fetch-modal__field label {
display: block;
font-size: 13px;
font-weight: 500;
margin-bottom: 4px;
color: #555;
}
.manual-fetch-modal__field input {
width: 100%;
padding: 8px 10px;
border: 1px solid #d0d0d0;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
}
.manual-fetch-modal__field input:disabled {
background-color: #f5f5f5;
cursor: not-allowed;
}
.manual-fetch-modal__info {
font-size: 13px;
color: #666;
margin-top: 8px;
padding: 8px;
background-color: #f9f9f9;
border-radius: 4px;
}
.manual-fetch-modal__footer {
display: flex;
gap: 8px;
justify-content: flex-end;
padding: 16px 20px;
border-top: 1px solid #e0e0e0;
background-color: #fafafa;
}
.manual-fetch-modal__button {
padding: 8px 16px;
border-radius: 4px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
border: none;
transition: background-color 0.2s;
}
.manual-fetch-modal__button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.manual-fetch-modal__button--primary {
background-color: #0066cc;
color: #fff;
}
.manual-fetch-modal__button--primary:hover:not(:disabled) {
background-color: #0052a3;
}
.manual-fetch-modal__button--secondary {
background-color: #e0e0e0;
color: #333;
}
.manual-fetch-modal__button--secondary:hover:not(:disabled) {
background-color: #d0d0d0;
}
@@ -0,0 +1,161 @@
.planned-day-modal__overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.planned-day-modal__content {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
.planned-day-modal__header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
border-bottom: 1px solid #e0e0e0;
}
.planned-day-modal__header h2 {
margin: 0;
font-size: 18px;
font-weight: 600;
}
.planned-day-modal__close {
background: none;
border: none;
font-size: 28px;
cursor: pointer;
padding: 0;
color: #666;
transition: color 0.2s;
}
.planned-day-modal__close:hover {
color: #000;
}
.planned-day-modal__body {
padding: 20px;
}
.planned-day-modal__date {
font-size: 14px;
color: #666;
margin: 0 0 16px 0;
font-weight: 500;
}
.planned-day-modal__error {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 16px;
font-size: 14px;
}
.planned-day-modal__field {
margin-bottom: 16px;
}
.planned-day-modal__label {
display: block;
font-size: 14px;
font-weight: 500;
margin-bottom: 6px;
color: #333;
}
.planned-day-modal__select {
width: 100%;
padding: 8px 10px;
border: 1px solid #d0d0d0;
border-radius: 4px;
font-size: 14px;
background-color: #fff;
cursor: pointer;
}
.planned-day-modal__select:disabled {
background-color: #f5f5f5;
cursor: not-allowed;
}
.planned-day-modal__checkbox-label {
display: flex;
align-items: center;
font-size: 14px;
cursor: pointer;
user-select: none;
}
.planned-day-modal__checkbox-label input[type='checkbox'] {
margin-right: 8px;
cursor: pointer;
}
.planned-day-modal__footer {
display: flex;
gap: 8px;
justify-content: flex-end;
padding: 16px 20px;
border-top: 1px solid #e0e0e0;
background-color: #fafafa;
}
.planned-day-modal__button {
padding: 8px 16px;
border-radius: 4px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
border: none;
transition: background-color 0.2s;
}
.planned-day-modal__button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.planned-day-modal__button--primary {
background-color: #0066cc;
color: #fff;
}
.planned-day-modal__button--primary:hover:not(:disabled) {
background-color: #0052a3;
}
.planned-day-modal__button--secondary {
background-color: #e0e0e0;
color: #333;
}
.planned-day-modal__button--secondary:hover:not(:disabled) {
background-color: #d0d0d0;
}
.planned-day-modal__button--danger {
background-color: #d32f2f;
color: #fff;
}
.planned-day-modal__button--danger:hover:not(:disabled) {
background-color: #b71c1c;
}
+16 -9
View File
@@ -7,7 +7,8 @@ interface UsePlannedDaysResult {
isLoading: boolean
error: string | null
refresh: () => Promise<void>
add: (date: string, childId: string, leaveType: string) => Promise<PlannedLeaveDay>
add: (payload: api.CreatePlannedDayPayload) => Promise<PlannedLeaveDay>
update: (id: string, payload: api.UpdatePlannedDayPayload) => Promise<PlannedLeaveDay>
remove: (id: string) => Promise<void>
}
@@ -32,15 +33,21 @@ export function usePlannedDays(childId?: string): UsePlannedDaysResult {
}, [childId])
const add = useCallback(
async (date: string, childId: string, leaveType: string) => {
const result = await api.createPlannedDay(childId, {
date,
leaveType,
})
async (payload: api.CreatePlannedDayPayload) => {
const result = await api.createPlannedDay(payload)
await fetch()
return result
},
[fetch]
[fetch],
)
const update = useCallback(
async (id: string, payload: api.UpdatePlannedDayPayload) => {
const result = await api.updatePlannedDay(id, payload)
await fetch()
return result
},
[fetch],
)
const remove = useCallback(
@@ -48,12 +55,12 @@ export function usePlannedDays(childId?: string): UsePlannedDaysResult {
await api.deletePlannedDay(id)
await fetch()
},
[fetch]
[fetch],
)
useEffect(() => {
fetch()
}, [fetch])
return { data, isLoading, error, refresh: fetch, add, remove }
return { data, isLoading, error, refresh: fetch, add, update, remove }
}
+66 -7
View File
@@ -11,6 +11,7 @@ import { ProgressChart } from '@/components/dashboard/ProgressChart'
import { ParentalLeaveBreakdown } from '@/components/dashboard/ParentalLeaveBreakdown'
import { ChildBirthdayInput } from '@/components/inputs/ChildBirthdayInput'
import { QRCodeModal } from '@/components/modals/QRCodeModal'
import { ManualFetchModal } from '@/components/modals/ManualFetchModal'
export function Dashboard(): ReactNode {
const { workspace, isLoading: wsLoading, selectedChildId, children, personalDataUuid, refreshContext } = useWorkspace()
@@ -28,6 +29,9 @@ export function Dashboard(): ReactNode {
const [isSubmitting, setIsSubmitting] = useState(false)
const [submitError, setSubmitError] = useState<string | null>(null)
const [showModal, setShowModal] = useState(false)
const [showManualFetchModal, setShowManualFetchModal] = useState(false)
const [isManualFetchSaving, setIsManualFetchSaving] = useState(false)
const [manualFetchError, setManualFetchError] = useState<string | null>(null)
const isLoading = wsLoading || analyticsLoading || pdLoading || bpLoading
@@ -64,6 +68,44 @@ export function Dashboard(): ReactNode {
await scraper.start(selectedChildId ?? undefined)
}
const handleSaveManualFetch = async (data: {
totalDays: number
daysUsed: number
reservedDaysLeft: number
transferableDaysLeft: number
dubbeldagarLeft: number
sjukpenningUsed: number
lagstaNivaUsed: number
}) => {
if (!workspace?.id || !selectedChildId || !personalDataUuid) return
try {
setIsManualFetchSaving(true)
setManualFetchError(null)
await api.saveManualFetch({
personalDataUuid,
workspaceId: workspace.id,
childId: selectedChildId,
totalDays: data.totalDays,
daysUsed: data.daysUsed,
reservedDaysLeft: data.reservedDaysLeft,
transferableDaysLeft: data.transferableDaysLeft,
dubbeldagarLeft: data.dubbeldagarLeft,
sjukpenningUsed: data.sjukpenningUsed,
lagstaNivaUsed: data.lagstaNivaUsed,
})
await refreshParentalLeave()
await refreshAnalytics()
setShowManualFetchModal(false)
} catch (err) {
setManualFetchError(err instanceof Error ? err.message : 'Failed to save manual data')
} finally {
setIsManualFetchSaving(false)
}
}
const handleModalClose = () => {
if (scraper.isRunning) return
setShowModal(false)
@@ -135,13 +177,22 @@ export function Dashboard(): ReactNode {
)}
<section className="dashboard__section">
<button
onClick={handleFetchFK}
disabled={!selectedChildId}
style={{ padding: '0.5rem 1rem', cursor: selectedChildId ? 'pointer' : 'not-allowed' }}
>
Hämta från Försäkringskassan
</button>
<div style={{ display: 'flex', gap: '10px' }}>
<button
onClick={handleFetchFK}
disabled={!selectedChildId}
style={{ padding: '0.5rem 1rem', cursor: selectedChildId ? 'pointer' : 'not-allowed' }}
>
Hämta från Försäkringskassan
</button>
<button
onClick={() => setShowManualFetchModal(true)}
disabled={!selectedChildId}
style={{ padding: '0.5rem 1rem', cursor: selectedChildId ? 'pointer' : 'not-allowed' }}
>
Ange uppgifter manuellt
</button>
</div>
</section>
<section className="dashboard__section">
@@ -199,6 +250,14 @@ export function Dashboard(): ReactNode {
isRunning={scraper.isRunning}
onCancel={scraper.cancel}
/>
<ManualFetchModal
isOpen={showManualFetchModal}
onSave={handleSaveManualFetch}
onClose={() => setShowManualFetchModal(false)}
isSaving={isManualFetchSaving}
error={manualFetchError}
/>
</div>
</Layout>
)
+53
View File
@@ -25,6 +25,7 @@ export function Settings(): ReactNode {
)
const [saveMessage, setSaveMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null)
const [isSaving, setIsSaving] = useState(false)
const [newPartnerName, setNewPartnerName] = useState('')
const isLoading = wsLoading || vacLoading || bpLoading
@@ -163,6 +164,30 @@ export function Settings(): ReactNode {
}
}
const handleAddPartner = async () => {
if (!newPartnerName.trim() || !workspace?.id) {
setSaveMessage({ type: 'error', text: 'Please enter a partner name' })
return
}
try {
setIsSaving(true)
setSaveMessage(null)
await api.addPartner(workspace.id, newPartnerName)
await refreshContext()
setNewPartnerName('')
setSaveMessage({ type: 'success', text: 'Partner added! Refresh the page to see changes.' })
setTimeout(() => setSaveMessage(null), 3000)
} catch (err) {
setSaveMessage({
type: 'error',
text: err instanceof Error ? err.message : 'Failed to add partner',
})
} finally {
setIsSaving(false)
}
}
return (
<Layout>
<div data-testid="settings-page" className="settings-page">
@@ -258,6 +283,34 @@ export function Settings(): ReactNode {
onRemove={handleRemoveBreakPeriod}
/>
</section>
<section className="settings-page__section">
<h2>Partner</h2>
<p>Add a partner to plan parental leave together.</p>
<div style={{ display: 'flex', gap: '8px' }}>
<input
type="text"
placeholder="Partner name"
value={newPartnerName}
onChange={(e) => setNewPartnerName(e.target.value)}
disabled={isSaving}
style={{
flex: 1,
padding: '8px 10px',
border: '1px solid #d0d0d0',
borderRadius: '4px',
}}
/>
<button
onClick={handleAddPartner}
className="settings-page__button"
disabled={isSaving}
data-testid="add-partner-button"
>
{isSaving ? 'Adding...' : 'Lägg till partner'}
</button>
</div>
</section>
</div>
</Layout>
)
+33 -5
View File
@@ -148,6 +148,16 @@ export async function updateWorkspace(
})
}
export async function addPartner(
workspaceId: string,
displayName: string,
): Promise<{ uuid: string; name: string; salary_type: string; employment_status: string }> {
return apiFetch(`/workspace/${workspaceId}/parents`, {
method: 'POST',
body: JSON.stringify({ display_name: displayName }),
})
}
// ---------------------------------------------------------------------------
// Vacation days (reference endpoint group — mirror this pattern for others)
// ---------------------------------------------------------------------------
@@ -289,13 +299,31 @@ export async function getPlannedDays(childId?: string): Promise<PlannedLeaveDay[
return apiFetch(`/planned-days${query}`)
}
export async function createPlannedDay(
childId: string,
payload: { date: string; leaveType: string },
): Promise<PlannedLeaveDay> {
export interface CreatePlannedDayPayload {
workspace_id: string
child_id: string
parent_personal_data_uuid: string
date: string
day_type: 'sjukpenning' | 'lagsta' | 'vacation'
is_dubbeldag?: boolean
}
export async function createPlannedDay(payload: CreatePlannedDayPayload): Promise<PlannedLeaveDay> {
return apiFetch(`/planned-days`, {
method: 'POST',
body: JSON.stringify({ ...payload, child_id: childId }),
body: JSON.stringify(payload),
})
}
export interface UpdatePlannedDayPayload {
day_type?: 'sjukpenning' | 'lagsta' | 'vacation'
is_dubbeldag?: boolean
}
export async function updatePlannedDay(id: string, payload: UpdatePlannedDayPayload): Promise<PlannedLeaveDay> {
return apiFetch(`/planned-days/${id}`, {
method: 'PUT',
body: JSON.stringify(payload),
})
}