Make RepairAdmin page responsive with mobile card layout

- Add responsive container layout with mobile-first approach
- Implement mobile card component for better touch interaction
- Switch between table (desktop) and cards (mobile) at sm breakpoint
- Make header section responsive with stacked layout on mobile
- Enhance drawer with full-screen mobile support
- Add mobile-optimized filter controls
- Improve touch targets and spacing for mobile devices
- Fix global CSS button border issue for card styling

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Clas Wen 2025-09-25 19:37:34 +08:00
parent 92396ccc4c
commit 3ef9cc2ebe
2 changed files with 129 additions and 40 deletions

View file

@ -140,15 +140,15 @@ function TicketDetailDrawer(props: {
return ( return (
<Drawer isOpen={isOpen} onOpenChange={onOpenChange}> <Drawer isOpen={isOpen} onOpenChange={onOpenChange}>
<DrawerContent> <DrawerContent>
<DrawerHeader> <DrawerHeader className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-2">
<h2 className="text-2xl font-bold"></h2> <h2 className="text-xl sm:text-2xl font-bold"></h2>
{isLoading} {isLoading && <span className="text-sm text-gray-500">{isLoading}</span>}
</DrawerHeader> </DrawerHeader>
<DrawerBody> <DrawerBody className="px-4 sm:px-6">
<EventDetail ref={eventDetailRef} eventId={props.event?.eventId}> <EventDetail ref={eventDetailRef} eventId={props.event?.eventId}>
{ {
event => ( event => (
<div className="mb-12 flex flex-col gap-2"> <div className="mb-8 sm:mb-12 flex flex-col gap-3 sm:gap-2">
{ {
availableActions?.map((action) => { availableActions?.map((action) => {
return ( return (
@ -171,8 +171,8 @@ function TicketDetailDrawer(props: {
} }
</EventDetail> </EventDetail>
</DrawerBody> </DrawerBody>
<DrawerFooter> <DrawerFooter className="px-4 sm:px-6">
<Button variant="flat" onPress={onClose}> <Button variant="flat" onPress={onClose} className="w-full sm:w-auto">
</Button> </Button>
</DrawerFooter> </DrawerFooter>
@ -336,6 +336,52 @@ export default function App() {
onOpen() onOpen()
} }
const MobileEventCard = ({ event }: { event: PublicEvent }) => (
<button className="bg-white border border-gray-200 rounded-lg p-4 shadow-sm" onClick={() => onOpenEventDetail(event)}>
<div className="mb-3 flex gap-2 items-center justify-between">
<div className="text font-medium text-gray-900 line-clamp-2">
{event.problem}
<span className="text font-medium text-gray-400 ml-1">#{event.eventId}</span>
</div>
<div className="">
<EventStatusChip status={event.status} size="sm" />
</div>
</div>
<div className="h-18">
<div className="flex items-center gap-2 text-sm text-gray-600">
<div className="">
{ dayjs(event.gmtCreate).format("YYYY-MM-DD HH:mm") }
</div>
{event.model && (
<div>
{event.model}
</div>
)}
<div>
{ event.size && <Chip size="sm">size:{event.size}</Chip>}
</div>
{event.member && (
<div className="flex items-center gap-2 ">
<User
avatarProps={{ radius: "full", src: event.member.avatar, size: "sm" }}
name=""
classNames={{
base: "justify-start",
name: "text-sm",
description: "text-xs",
}}
/>
</div>
)}
</div>
</div>
</button>
)
const renderCell = useCallback((event: PublicEvent, columnKey: string | number) => { const renderCell = useCallback((event: PublicEvent, columnKey: string | number) => {
const cellValue = event[columnKey] const cellValue = event[columnKey]
@ -389,16 +435,59 @@ export default function App() {
} }
}, []) }, [])
return ( return (
<section className="box-border max-w-[1024px] mx-auto px-[22px] mb-24"> <section className="box-border max-w-full px-4 sm:px-6 lg:max-w-[1024px] lg:px-[22px] mx-auto mb-16 sm:mb-24">
<div className="mt-6 flex justify-between items-center"> <div className="mt-6 flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
<div className="text-2xl font-bold"></div> <div className="text-xl sm:text-2xl font-bold"></div>
{ {
userInfo?.roles?.find(v => v.toLowerCase() == "repair admin") userInfo?.roles?.find(v => v.toLowerCase() == "repair admin")
? <ExportExcelModal></ExportExcelModal> ? <div className="w-full sm:w-auto"><ExportExcelModal></ExportExcelModal></div>
: <></> : <></>
} }
</div> </div>
<div className="my-8 flex flex-col gap-4"> <div className="my-8 flex flex-col gap-4">
{/* Mobile Cards Layout */}
<div className="block sm:hidden">
{/* Filter Section for Mobile */}
<div className="mb-4 flex items-center gap-2">
<span className="text-sm font-medium">:</span>
<CheckboxPopover value={statusFilter} onValueChange={setStatusFilter} />
</div>
{isLoading
? (
<div className="flex justify-center py-8">
<Spinner label="Loading..." />
</div>
)
: (
<div className="flex flex-col gap-4">
{items.map(event => (
<MobileEventCard key={event.eventId} event={event} />
))}
{items.length === 0 && (
<div className="text-center py-8 text-gray-500">
</div>
)}
</div>
)}
{/* Mobile Pagination */}
<div className="mt-6 flex justify-center">
<Pagination
isCompact
showControls
showShadow
color="secondary"
page={page}
total={pages}
onChange={page => setPage(page)}
/>
</div>
</div>
{/* Desktop Table Layout */}
<div className="hidden sm:block">
<Table <Table
aria-label="Example table with dynamic content" aria-label="Example table with dynamic content"
sortDescriptor={list.sortDescriptor} sortDescriptor={list.sortDescriptor}
@ -429,6 +518,7 @@ export default function App() {
</TableBody> </TableBody>
</Table> </Table>
</div> </div>
</div>
<TicketDetailDrawer <TicketDetailDrawer
event={activeEvent} event={activeEvent}
onEventUpdated={list.reload} onEventUpdated={list.reload}

View file

@ -122,7 +122,6 @@ progress {
button { button {
background: none; background: none;
border: 0;
box-sizing: border-box; box-sizing: border-box;
color: inherit; color: inherit;
cursor: pointer; cursor: pointer;