mirror of
https://github.com/m1ngsama/FUJI.git
synced 2025-12-25 02:56:38 +00:00
refactor: convert notification preferences to modal dialog
- Convert NotificationPreferences from inline Card to Modal dialog - Add button next to export button in repair admin header - Load preferences on modal open instead of component mount - Add close button in modal footer - Improve mobile responsiveness with button grouping
This commit is contained in:
parent
c6e5a9535a
commit
eb9b68e753
2 changed files with 91 additions and 68 deletions
|
|
@ -1,5 +1,14 @@
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { Card, CardBody, CardHeader, Switch, Spinner, Button } from "@heroui/react"
|
import {
|
||||||
|
Modal,
|
||||||
|
ModalContent,
|
||||||
|
ModalHeader,
|
||||||
|
ModalBody,
|
||||||
|
ModalFooter,
|
||||||
|
Switch,
|
||||||
|
Spinner,
|
||||||
|
Button,
|
||||||
|
} from "@heroui/react"
|
||||||
import { saturdayClient } from "../../utils/client"
|
import { saturdayClient } from "../../utils/client"
|
||||||
import type { components } from "../../types/saturday"
|
import type { components } from "../../types/saturday"
|
||||||
|
|
||||||
|
|
@ -11,15 +20,23 @@ interface NotificationPreferencesProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NotificationPreferences({ token }: NotificationPreferencesProps) {
|
export default function NotificationPreferences({ token }: NotificationPreferencesProps) {
|
||||||
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
const [preferences, setPreferences] = useState<NotificationPreferenceItem[]>([])
|
const [preferences, setPreferences] = useState<NotificationPreferenceItem[]>([])
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [isSaving, setIsSaving] = useState(false)
|
const [isSaving, setIsSaving] = useState(false)
|
||||||
const [errorMessage, setErrorMessage] = useState<string>("")
|
const [errorMessage, setErrorMessage] = useState<string>("")
|
||||||
const [successMessage, setSuccessMessage] = useState<string>("")
|
const [successMessage, setSuccessMessage] = useState<string>("")
|
||||||
|
|
||||||
useEffect(() => {
|
const openModal = () => {
|
||||||
|
setIsOpen(true)
|
||||||
loadPreferences()
|
loadPreferences()
|
||||||
}, [token])
|
}
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
setIsOpen(false)
|
||||||
|
setErrorMessage("")
|
||||||
|
setSuccessMessage("")
|
||||||
|
}
|
||||||
|
|
||||||
const loadPreferences = async () => {
|
const loadPreferences = async () => {
|
||||||
if (!token) return
|
if (!token) return
|
||||||
|
|
@ -99,60 +116,73 @@ export default function NotificationPreferences({ token }: NotificationPreferenc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return (
|
|
||||||
<Card className="w-full">
|
|
||||||
<CardBody className="flex justify-center items-center py-8">
|
|
||||||
<Spinner label="加载中..." />
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="w-full">
|
<>
|
||||||
<CardHeader className="flex flex-col items-start gap-2 pb-4">
|
<Button onPress={openModal} color="primary" variant="flat">
|
||||||
<h3 className="text-lg sm:text-xl font-bold">通知偏好设置</h3>
|
通知设置
|
||||||
<p className="text-sm text-gray-500">管理您希望接收的通知类型</p>
|
</Button>
|
||||||
</CardHeader>
|
|
||||||
<CardBody className="gap-4">
|
|
||||||
{preferences.map((pref) => (
|
|
||||||
<div
|
|
||||||
key={pref.notificationType}
|
|
||||||
className="flex flex-col sm:flex-row sm:items-center justify-between gap-2 sm:gap-4 py-2 border-b border-gray-100 last:border-0"
|
|
||||||
>
|
|
||||||
<div className="flex-1">
|
|
||||||
<p className="text-sm sm:text-base font-medium">{pref.description}</p>
|
|
||||||
<p className="text-xs text-gray-400 mt-1">{pref.notificationType}</p>
|
|
||||||
</div>
|
|
||||||
<Switch
|
|
||||||
isSelected={pref.enabled}
|
|
||||||
onValueChange={(value) => handleToggle(pref.notificationType, value)}
|
|
||||||
isDisabled={isSaving}
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{/* Messages */}
|
<Modal isOpen={isOpen} onClose={closeModal} size="2xl">
|
||||||
{errorMessage && (
|
<ModalContent>
|
||||||
<div className="bg-red-50 border border-red-200 rounded-lg p-3 mt-2">
|
<ModalHeader className="flex flex-col gap-1">
|
||||||
<p className="text-sm text-red-800">{errorMessage}</p>
|
<h3 className="text-xl font-bold">通知偏好设置</h3>
|
||||||
</div>
|
<p className="text-sm text-gray-500 font-normal">管理您希望接收的通知类型</p>
|
||||||
)}
|
</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="flex justify-center items-center py-8">
|
||||||
|
<Spinner label="加载中..." />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
{preferences.map((pref) => (
|
||||||
|
<div
|
||||||
|
key={pref.notificationType}
|
||||||
|
className="flex flex-col sm:flex-row sm:items-center justify-between gap-2 sm:gap-4 py-3 border-b border-gray-100 last:border-0"
|
||||||
|
>
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="text-sm sm:text-base font-medium">{pref.description}</p>
|
||||||
|
<p className="text-xs text-gray-400 mt-1">{pref.notificationType}</p>
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
isSelected={pref.enabled}
|
||||||
|
onValueChange={(value) => handleToggle(pref.notificationType, value)}
|
||||||
|
isDisabled={isSaving}
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
{successMessage && (
|
{preferences.length === 0 && !errorMessage && (
|
||||||
<div className="bg-green-50 border border-green-200 rounded-lg p-3 mt-2">
|
<div className="text-center py-4 text-gray-500">
|
||||||
<p className="text-sm text-green-800">{successMessage}</p>
|
<p className="text-sm">暂无可用的通知偏好设置</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{preferences.length === 0 && !errorMessage && (
|
{/* Messages */}
|
||||||
<div className="text-center py-4 text-gray-500">
|
{errorMessage && (
|
||||||
<p className="text-sm">暂无可用的通知偏好设置</p>
|
<div className="bg-red-50 border border-red-200 rounded-lg p-3 mt-2">
|
||||||
</div>
|
<p className="text-sm text-red-800">{errorMessage}</p>
|
||||||
)}
|
</div>
|
||||||
</CardBody>
|
)}
|
||||||
</Card>
|
|
||||||
|
{successMessage && (
|
||||||
|
<div className="bg-green-50 border border-green-200 rounded-lg p-3 mt-2">
|
||||||
|
<p className="text-sm text-green-800">{successMessage}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button variant="flat" onPress={closeModal}>
|
||||||
|
关闭
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -526,20 +526,13 @@ export default function App() {
|
||||||
<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">
|
<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 flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
<div className="mt-6 flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
||||||
<div className="text-xl sm:text-2xl font-bold">维修管理</div>
|
<div className="text-xl sm:text-2xl font-bold">维修管理</div>
|
||||||
{
|
<div className="flex gap-2 w-full sm:w-auto">
|
||||||
userInfo?.roles?.find(v => v.toLowerCase() == "repair admin")
|
{token && <NotificationPreferences token={token} />}
|
||||||
? <div className="w-full sm:w-auto"><ExportExcelModal></ExportExcelModal></div>
|
{userInfo?.roles?.find(v => v.toLowerCase() == "repair admin") && (
|
||||||
: <></>
|
<ExportExcelModal />
|
||||||
}
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Notification Preferences Section */}
|
|
||||||
{token && (
|
|
||||||
<div className="mt-6">
|
|
||||||
<NotificationPreferences token={token} />
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
|
||||||
<div className="my-8 flex flex-col gap-4">
|
<div className="my-8 flex flex-col gap-4">
|
||||||
{/* Mobile Cards Layout */}
|
{/* Mobile Cards Layout */}
|
||||||
<div className="block sm:hidden">
|
<div className="block sm:hidden">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue