AdminDepartments: light theme, group by location, location_name support
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f3d46b8c80
commit
1dcf8bfbcb
1 changed files with 68 additions and 42 deletions
|
|
@ -6,6 +6,8 @@ interface WfDept {
|
|||
id: string
|
||||
name: string
|
||||
staff_count: number
|
||||
location_id: string | null
|
||||
location_name: string | null
|
||||
mapped_role_id: number | null
|
||||
}
|
||||
|
||||
|
|
@ -80,6 +82,16 @@ export function AdminDepartments({ user }: { user: User }) {
|
|||
}
|
||||
}
|
||||
|
||||
// Group departments by location
|
||||
const locations = Array.from(new Set(depts.map(d => d.location_name ?? ''))).filter(Boolean)
|
||||
const hasMultipleLocations = locations.length > 1
|
||||
const grouped: Record<string, WfDept[]> = {}
|
||||
for (const d of depts) {
|
||||
const key = d.location_name ?? ''
|
||||
if (!grouped[key]) grouped[key] = []
|
||||
grouped[key].push(d)
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', height: '100dvh' }}>
|
||||
<Sidebar user={user} />
|
||||
|
|
@ -96,53 +108,67 @@ export function AdminDepartments({ user }: { user: User }) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{syncResult && (
|
||||
<div style={{ fontSize: '0.82rem', color: 'var(--text-muted)', marginBottom: '1rem',
|
||||
background: 'var(--surface)', borderRadius: '6px', padding: '0.6rem 0.875rem',
|
||||
border: '1px solid var(--surface-2)' }}>
|
||||
{syncResult}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p style={{ fontSize: '0.82rem', color: 'var(--text-muted)', marginBottom: '1.25rem' }}>
|
||||
<p style={{ fontSize: '0.82rem', color: 'var(--text-mid)', marginBottom: syncResult ? '0.5rem' : '1.25rem' }}>
|
||||
Map Workforce departments to roles. Self-registering employees will automatically
|
||||
receive the role mapped to their department.
|
||||
</p>
|
||||
|
||||
{error && <p style={{ color: 'var(--danger)', fontSize: '0.85rem', marginBottom: '1rem' }}>{error}</p>}
|
||||
{syncResult && (
|
||||
<div style={{ fontSize: '0.82rem', color: 'var(--text-mid)', marginBottom: '1rem',
|
||||
background: 'var(--card-bg)', borderRadius: '6px', padding: '0.6rem 0.875rem',
|
||||
border: '1px solid var(--card-border)' }}>
|
||||
{syncResult}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && <p style={{ color: '#dc2626', fontSize: '0.85rem', marginBottom: '1rem' }}>{error}</p>}
|
||||
|
||||
{loading ? (
|
||||
<p style={{ color: 'var(--text-muted)', fontSize: '0.875rem' }}>Loading departments…</p>
|
||||
<p style={{ color: 'var(--text-mid)', fontSize: '0.875rem' }}>Loading departments…</p>
|
||||
) : (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
|
||||
{depts.map(dept => (
|
||||
<div key={dept.id} style={{
|
||||
background: 'var(--surface)', borderRadius: '8px',
|
||||
padding: '0.75rem 1rem', border: '1px solid var(--surface-2)',
|
||||
display: 'flex', alignItems: 'center', gap: '1rem', flexWrap: 'wrap',
|
||||
}}>
|
||||
<div style={{ flex: 1, minWidth: '140px' }}>
|
||||
<div style={{ fontWeight: 600, fontSize: '0.875rem' }}>{dept.name}</div>
|
||||
<div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>
|
||||
{dept.staff_count} staff
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: hasMultipleLocations ? '1.5rem' : '0.5rem' }}>
|
||||
{Object.entries(grouped).map(([location, locationDepts]) => (
|
||||
<div key={location || 'default'}>
|
||||
{hasMultipleLocations && location && (
|
||||
<div style={{ fontSize: '0.72rem', fontWeight: 700, textTransform: 'uppercase',
|
||||
letterSpacing: '0.06em', color: 'var(--text-mid)', marginBottom: '0.5rem',
|
||||
paddingBottom: '0.25rem', borderBottom: '1px solid var(--card-border)' }}>
|
||||
{location}
|
||||
</div>
|
||||
</div>
|
||||
<select
|
||||
value={mappings[dept.id] ?? ''}
|
||||
onChange={e => setMappings(m => ({
|
||||
...m, [dept.id]: e.target.value ? Number(e.target.value) : null
|
||||
}))}
|
||||
style={{
|
||||
background: 'var(--navy-dark)', border: '1px solid var(--surface-2)',
|
||||
borderRadius: '6px', color: 'var(--text)', padding: '0.4rem 0.6rem',
|
||||
fontSize: '0.82rem', minWidth: '160px',
|
||||
}}
|
||||
>
|
||||
<option value="">— No role —</option>
|
||||
{roles.map(r => (
|
||||
<option key={r.id} value={r.id}>{r.name}</option>
|
||||
)}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
|
||||
{locationDepts.map(dept => (
|
||||
<div key={dept.id} style={{
|
||||
background: 'var(--card-bg)', borderRadius: '8px',
|
||||
padding: '0.75rem 1rem', border: '1px solid var(--card-border)',
|
||||
boxShadow: 'var(--shadow-sm)',
|
||||
display: 'flex', alignItems: 'center', gap: '1rem', flexWrap: 'wrap',
|
||||
}}>
|
||||
<div style={{ flex: 1, minWidth: '140px' }}>
|
||||
<div style={{ fontWeight: 600, fontSize: '0.875rem' }}>{dept.name}</div>
|
||||
<div style={{ fontSize: '0.75rem', color: 'var(--text-mid)' }}>
|
||||
{dept.staff_count} staff
|
||||
</div>
|
||||
</div>
|
||||
<select
|
||||
value={mappings[dept.id] ?? ''}
|
||||
onChange={e => setMappings(m => ({
|
||||
...m, [dept.id]: e.target.value ? Number(e.target.value) : null
|
||||
}))}
|
||||
style={{
|
||||
background: 'var(--body-bg)', border: '1px solid var(--card-border)',
|
||||
borderRadius: '6px', color: 'var(--text-dark)', padding: '0.4rem 0.6rem',
|
||||
fontSize: '0.82rem', minWidth: '160px',
|
||||
}}
|
||||
>
|
||||
<option value="">— No role —</option>
|
||||
{roles.map(r => (
|
||||
<option key={r.id} value={r.id}>{r.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -153,10 +179,10 @@ export function AdminDepartments({ user }: { user: User }) {
|
|||
}
|
||||
|
||||
const goldBtn: React.CSSProperties = {
|
||||
background: 'var(--gold)', color: 'var(--navy-dark)', border: 'none',
|
||||
borderRadius: '6px', padding: '0.5rem 1rem', fontSize: '0.875rem', fontWeight: 600,
|
||||
background: 'var(--navy)', color: '#fff', border: 'none',
|
||||
borderRadius: '6px', padding: '0.5rem 1rem', fontSize: '0.875rem', fontWeight: 600, cursor: 'pointer',
|
||||
}
|
||||
const mutedBtn: React.CSSProperties = {
|
||||
background: 'var(--surface-2)', color: 'var(--text-muted)', border: '1px solid var(--surface-2)',
|
||||
borderRadius: '6px', padding: '0.5rem 1rem', fontSize: '0.875rem',
|
||||
background: 'var(--body-bg)', color: 'var(--text-mid)', border: '1px solid var(--card-border)',
|
||||
borderRadius: '6px', padding: '0.5rem 1rem', fontSize: '0.875rem', cursor: 'pointer',
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue