Files
loyaly-merchant/src/features/settings/components/TeamManagement.tsx

301 lines
8.5 KiB
TypeScript

'use client';
import {useState} from 'react';
import {proportional, pixel} from '@astryxdesign/core/Table';
import {ResponsiveTable} from '@/shared/components/patterns/ResponsiveTable';
import type {TableColumn} from '@astryxdesign/core/Table';
import {VStack, HStack} from '@astryxdesign/core/Layout';
import {Text, Heading} from '@astryxdesign/core/Text';
import {Badge} from '@astryxdesign/core/Badge';
import {Button} from '@astryxdesign/core/Button';
import {Icon} from '@astryxdesign/core/Icon';
import {Avatar} from '@astryxdesign/core/Avatar';
import {DropdownMenu} from '@astryxdesign/core/DropdownMenu';
import {TextInput} from '@astryxdesign/core/TextInput';
import {Selector} from '@astryxdesign/core/Selector';
import {useToast} from '@astryxdesign/core/Toast';
import {StaticPanel} from '@/shared/components/patterns/PanelCard';
export interface StaffUser extends Record<string, unknown> {
id: string;
name: string;
email: string;
role: 'Owner' | 'Manager' | 'Cashier' | 'Marketing' | 'Support';
storeName: string;
status: 'active' | 'suspended' | 'invited';
lastLogin: string;
}
const INITIAL_STAFF: StaffUser[] = [
{
id: 'usr-1',
name: 'Aravind',
email: 'aravind@nearle.in',
role: 'Owner',
storeName: 'All stores',
status: 'active',
lastLogin: 'Just now',
},
{
id: 'usr-2',
name: 'Vikram Seth',
email: 'vikram@nearle.in',
role: 'Manager',
storeName: 'Indiranagar Flagship',
status: 'active',
lastLogin: 'Today, 14:20',
},
{
id: 'usr-3',
name: 'Priya Sharma',
email: 'priya@nearle.in',
role: 'Cashier',
storeName: 'Koramangala 80ft',
status: 'active',
lastLogin: 'Today, 09:15',
},
{
id: 'usr-4',
name: 'Rahul Verma',
email: 'rahul@nearle.in',
role: 'Marketing',
storeName: 'All stores',
status: 'invited',
lastLogin: 'Pending accept',
},
{
id: 'usr-5',
name: 'Deepa Nair',
email: 'deepa@nearle.in',
role: 'Cashier',
storeName: 'Whitefield Main',
status: 'suspended',
lastLogin: '3 days ago',
},
];
export function TeamManagement() {
const toast = useToast();
const [staffList, setStaffList] = useState<StaffUser[]>(INITIAL_STAFF);
const [isAdding, setIsAdding] = useState(false);
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [role, setRole] = useState<'Owner' | 'Manager' | 'Cashier' | 'Marketing' | 'Support'>('Manager');
const [storeName, setStoreName] = useState('Indiranagar Flagship');
const handleAddStaff = () => {
if (!name.trim() || !email.trim()) {
toast({type: 'error', body: 'Name and Email are required'});
return;
}
const newUser: StaffUser = {
id: `usr-${Date.now()}`,
name,
email,
role,
storeName,
status: 'invited',
lastLogin: 'Pending accept',
};
setStaffList((prev) => [newUser, ...prev]);
setName('');
setEmail('');
setIsAdding(false);
toast({body: `Invitation sent to ${email}`});
};
const handleToggleStatus = (id: string) => {
setStaffList((prev) =>
prev.map((s) => {
if (s.id !== id) return s;
const nextStatus = s.status === 'suspended' ? 'active' : 'suspended';
toast({body: `${s.name} status updated to ${nextStatus}`});
return {...s, status: nextStatus};
}),
);
};
const handleResetPassword = (emailStr: string) => {
toast({body: `Password reset link sent to ${emailStr}`});
};
const handleRemove = (id: string, nameStr: string) => {
setStaffList((prev) => prev.filter((s) => s.id !== id));
toast({body: `${nameStr} removed from team`});
};
const columns: TableColumn<StaffUser>[] = [
{
key: 'name',
header: 'Staff Member',
width: proportional(2),
renderCell: (row) => (
<HStack gap={3} vAlign="center">
<Avatar name={row.name} size="sm" tooltip={false} />
<VStack gap={0}>
<Text size="sm" weight="medium">
{row.name}
</Text>
<Text size="sm" color="secondary">
{row.email}
</Text>
</VStack>
</HStack>
),
},
{
key: 'role',
header: 'Role',
width: proportional(1),
renderCell: (row) => (
<Badge
variant={row.role === 'Owner' ? 'info' : 'neutral'}
label={row.role}
/>
),
},
{
key: 'storeName',
header: 'Assigned Store',
width: proportional(1.5),
renderCell: (row) => <Text size="sm">{row.storeName}</Text>,
},
{
key: 'status',
header: 'Status',
width: pixel(110),
renderCell: (row) => {
const v =
row.status === 'active'
? 'success'
: row.status === 'suspended'
? 'error'
: 'warning';
return <Badge variant={v} label={row.status} />;
},
},
{
key: 'lastLogin',
header: 'Last Login',
width: proportional(1.2),
renderCell: (row) => (
<Text size="sm" color="secondary">
{row.lastLogin}
</Text>
),
},
{
key: 'actions',
header: 'Actions',
align: 'center',
width: pixel(160),
renderCell: (row) =>
row.role === 'Owner' ? (
<HStack hAlign="center">
<Text size="sm" color="secondary">
Primary Owner
</Text>
</HStack>
) : (
<HStack hAlign="center">
<DropdownMenu
button={{
variant: 'secondary',
size: 'sm',
label: 'Manage',
icon: <Icon icon="moreHorizontal" size="sm" />,
}}
menuWidth={180}
items={[
{
label: row.status === 'suspended' ? 'Reactivate staff' : 'Suspend staff',
onClick: () => handleToggleStatus(row.id),
},
{
label: 'Reset password',
onClick: () => handleResetPassword(row.email),
},
{type: 'divider'},
{
label: 'Remove from team',
onClick: () => handleRemove(row.id, row.name),
},
]}
/>
</HStack>
),
},
];
return (
<VStack gap={5}>
<StaticPanel
title="Team Directory & Staff Access"
subtitle="Manage store assignments, role permissions, password resets and account statuses."
actions={
<Button
variant="primary"
size="sm"
label={isAdding ? 'Cancel' : 'Add Staff Member'}
onClick={() => setIsAdding(!isAdding)}
/>
}
>
<VStack gap={4}>
{isAdding ? (
<VStack gap={3} padding={4}>
<Heading level={3}>Invite New Staff Member</Heading>
<HStack gap={3} vAlign="end" wrap="wrap">
<TextInput
label="Full Name"
value={name}
onChange={setName}
placeholder="e.g. Ananya Rao"
/>
<TextInput
label="Work Email"
value={email}
onChange={setEmail}
placeholder="ananya@nearle.in"
/>
<Selector
label="Role"
value={role}
onChange={(v) =>
setRole(v as 'Owner' | 'Manager' | 'Cashier' | 'Marketing' | 'Support')
}
options={['Manager', 'Cashier', 'Marketing', 'Support']}
/>
<Selector
label="Assigned Store"
value={storeName}
onChange={(v) => setStoreName(v)}
options={[
'All stores',
'Indiranagar Flagship',
'Koramangala 80ft',
'Whitefield Main',
'Jayanagar 4th Block',
]}
/>
<Button
variant="primary"
size="sm"
label="Send Invite"
onClick={handleAddStaff}
/>
</HStack>
</VStack>
) : null}
<ResponsiveTable
columns={columns}
data={staffList}
primaryKey="name"
/>
</VStack>
</StaticPanel>
</VStack>
);
}