From 446a7435e6bf776b513eaf72212005b74b24f1ec Mon Sep 17 00:00:00 2001 From: Adrian Andersen Date: Tue, 17 Dec 2024 01:08:02 +0100 Subject: [PATCH] feat: inital design for reminder page --- src/app/admin/hurtigutdeling/page.tsx | 39 +++--- .../kommunikasjon/overleveringer/page.tsx | 7 +- .../admin/kommunikasjon/paaminnelser/page.tsx | 53 +++++++- src/app/admin/kommunikasjon/page.tsx | 38 +++++- src/app/admin/layout.tsx | 8 +- src/app/admin/start/page.tsx | 20 ++- src/components/AdminNavigationCards.tsx | 78 ++++++------ src/components/RapidHandout.tsx | 37 ------ .../communication/CustomerItemTypePicker.tsx | 32 +++++ .../admin/communication/DeadlinePicker.tsx | 93 ++++++++++++++ .../communication/MessageMethodPicker.tsx | 33 +++++ .../admin/communication/MultiBranchPicker.tsx | 119 ++++++++++++++++++ ...PageNavigation.tsx => adminNavigation.tsx} | 29 +++-- 13 files changed, 449 insertions(+), 137 deletions(-) delete mode 100644 src/components/RapidHandout.tsx create mode 100644 src/components/admin/communication/CustomerItemTypePicker.tsx create mode 100644 src/components/admin/communication/DeadlinePicker.tsx create mode 100644 src/components/admin/communication/MessageMethodPicker.tsx create mode 100644 src/components/admin/communication/MultiBranchPicker.tsx rename src/utils/{adminPageNavigation.tsx => adminNavigation.tsx} (87%) diff --git a/src/app/admin/hurtigutdeling/page.tsx b/src/app/admin/hurtigutdeling/page.tsx index 333ea20..5deca9c 100644 --- a/src/app/admin/hurtigutdeling/page.tsx +++ b/src/app/admin/hurtigutdeling/page.tsx @@ -1,30 +1,21 @@ -import { Card, Container, Typography } from "@mui/material"; -import { Box } from "@mui/system"; -import { Metadata } from "next"; +"use client"; +import { UserDetail } from "@boklisten/bl-model"; +import { PageContainer } from "@toolpad/core"; +import { useState } from "react"; -import RapidHandout from "@/components/RapidHandout"; - -export const metadata: Metadata = { - title: "Hurtigutdeling", - description: "Adminverktøy for å raskt dele ut bøker.", -}; +import RapidHandoutDetails from "@/components/RapidHandoutDetails"; +import UserDetailSearchField from "@/components/search/UserDetailSearchField"; export default function HandoutPage() { + const [customer, setCustomer] = useState(null); return ( - - - - Hurtigutdeling - - - - + + { + setCustomer(userDetail); + }} + /> + {customer && } + ); } diff --git a/src/app/admin/kommunikasjon/overleveringer/page.tsx b/src/app/admin/kommunikasjon/overleveringer/page.tsx index 565cd4e..56e9b76 100644 --- a/src/app/admin/kommunikasjon/overleveringer/page.tsx +++ b/src/app/admin/kommunikasjon/overleveringer/page.tsx @@ -1,9 +1,6 @@ "use client"; -import { redirect } from "next/navigation"; - -import { attachTokensToHref } from "@/components/AuthLinker"; -import BL_CONFIG from "@/utils/bl-config"; +import { PageContainer } from "@toolpad/core"; export default function MatchesCommunicationPage() { - redirect(attachTokensToHref(BL_CONFIG.blAdmin.basePath + "messenger/match")); + return matches; } diff --git a/src/app/admin/kommunikasjon/paaminnelser/page.tsx b/src/app/admin/kommunikasjon/paaminnelser/page.tsx index 7122d73..629b5cd 100644 --- a/src/app/admin/kommunikasjon/paaminnelser/page.tsx +++ b/src/app/admin/kommunikasjon/paaminnelser/page.tsx @@ -1,11 +1,54 @@ "use client"; -import { redirect } from "next/navigation"; +import { CustomerItemType } from "@boklisten/bl-model"; +import { MessageMethod } from "@boklisten/bl-model/message/message-method/message-method"; +import { Grid2, Typography } from "@mui/material"; +import { PageContainer } from "@toolpad/core"; +import { useState } from "react"; -import { attachTokensToHref } from "@/components/AuthLinker"; -import BL_CONFIG from "@/utils/bl-config"; +import CustomerItemTypePicker from "@/components/admin/communication/CustomerItemTypePicker"; +import DeadlinePicker from "@/components/admin/communication/DeadlinePicker"; +import MessageMethodPicker from "@/components/admin/communication/MessageMethodPicker"; +import MultiBranchPicker from "@/components/admin/communication/MultiBranchPicker"; export default function RemindersPage() { - redirect( - attachTokensToHref(BL_CONFIG.blAdmin.basePath + "messenger/reminder"), + const [deadline, setDeadline] = useState(null); + const [customerItemType, setCustomerItemType] = + useState(null); + const [branchIDs, setBranchIDs] = useState([]); + const [messageMethod, setMessageMethod] = useState( + null, + ); + + return ( + + + { + setBranchIDs(newBranchIDs); + }} + /> + { + setDeadline(newDeadline); + }} + /> + + { + setCustomerItemType(newCustomerItemType); + }} + /> + { + setMessageMethod(newMessageMethod); + }} + /> + + Schools: {branchIDs.join(", ")} + Deadline: {String(deadline ?? "")} + CustomerItemType: {customerItemType ?? ""} + MessageMethod: {messageMethod ?? ""} + + ); } diff --git a/src/app/admin/kommunikasjon/page.tsx b/src/app/admin/kommunikasjon/page.tsx index 7cd4869..7d0ea2e 100644 --- a/src/app/admin/kommunikasjon/page.tsx +++ b/src/app/admin/kommunikasjon/page.tsx @@ -1,10 +1,38 @@ "use client"; -import { redirect } from "next/navigation"; +import { PageContainer } from "@toolpad/core"; -import { attachTokensToHref } from "@/components/AuthLinker"; -import BL_CONFIG from "@/utils/bl-config"; +import AdminNavigationCards from "@/components/AdminNavigationCards"; +import { COMMUNICATION_SUB_PAGES } from "@/utils/adminNavigation"; export default function CommunicationRootPage() { - redirect(attachTokensToHref(BL_CONFIG.blAdmin.basePath + "messenger")); - // apply permission guard once implemented + /** + * TODO: estimat på antall mottakere + * + * Flow: + * Velg kundegruppe + * kunder med bokfrist | kunder med overleveringer + * + * > Kunder med bokfrist + * => Velg FRIST (Toggle buttons / date picker) + * => Velg elevgruppe (privatist/vgs) + * => Velg Skoler (Toggle button (alle / multi-select) + * => Velg SMS eller E-post + * >>> SMS => Fritekst + * >>> E-post (TemplateID) + * + * > Kunder med overleveringer + * => Velg Skoler (Toggle button (alle / multi-select) + * => Velg ALLE / sendere / mottakere / kun stand + * => Velg SMS eller E-post (templateID) + * + */ + return ( + + + + ); } diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx index c39a2e3..452a64d 100644 --- a/src/app/admin/layout.tsx +++ b/src/app/admin/layout.tsx @@ -6,7 +6,7 @@ import { ReactNode, useEffect, useState } from "react"; import { getUserPermission } from "@/api/auth"; import PagePermissionGuard from "@/components/PagePermissionGuard"; -import { getAdminPagesNavigationLinks } from "@/utils/adminPageNavigation"; +import { getAdminPagesNavigationLinks } from "@/utils/adminNavigation"; import theme from "@/utils/theme"; export default function AdminLayout({ children }: { children: ReactNode }) { @@ -32,11 +32,7 @@ export default function AdminLayout({ children }: { children: ReactNode }) { homeUrl: "start", }} > - - {children} - + {children} ); diff --git a/src/app/admin/start/page.tsx b/src/app/admin/start/page.tsx index b004f3e..49325a8 100644 --- a/src/app/admin/start/page.tsx +++ b/src/app/admin/start/page.tsx @@ -1,20 +1,28 @@ "use client"; import { Typography } from "@mui/material"; -import { PageContainer } from "@toolpad/core"; +import { Navigation, PageContainer } from "@toolpad/core"; +import { useEffect, useState } from "react"; +import { getUserPermission } from "@/api/auth"; import AdminNavigationCards from "@/components/AdminNavigationCards"; +import { getAdminPagesNavigationLinks } from "@/utils/adminNavigation"; export default function AdminStartPage() { + const [navLinks, setNavLinks] = useState([]); + useEffect(() => { + const userPermission = getUserPermission(); + setNavLinks(getAdminPagesNavigationLinks(userPermission)); + }, []); return ( - + Velkommen til bl-admin, Boklisten sitt administrasjonssystem for bøker! - - Trykk på et verktøy for å komme i gang! - - + ); } diff --git a/src/components/AdminNavigationCards.tsx b/src/components/AdminNavigationCards.tsx index 4adf42b..1ca7697 100644 --- a/src/components/AdminNavigationCards.tsx +++ b/src/components/AdminNavigationCards.tsx @@ -8,46 +8,50 @@ import { Typography, } from "@mui/material"; import { Navigation, NavigationPageItem } from "@toolpad/core"; -import { useEffect, useState } from "react"; -import { getUserPermission } from "@/api/auth"; import DynamicLink from "@/components/DynamicLink"; -import { getAdminPagesNavigationLinks } from "@/utils/adminPageNavigation"; - -export default function AdminNavigationCards() { - const [navLinks, setNavLinks] = useState([]); - useEffect(() => { - const userPermission = getUserPermission(); - setNavLinks(getAdminPagesNavigationLinks(userPermission)); - }, []); +export default function AdminNavigationCards({ + navLinks, + rootPath, + label, +}: { + navLinks: Navigation; + label: string; + rootPath?: string; +}) { return ( - - {navLinks - .filter( - (navLink): navLink is NavigationPageItem => navLink.kind === "page", - ) - .map((navLink) => ( - - - - - - - {navLink.icon} - {navLink.title} - - - - - - - ))} - + <> + + {label} + + + {navLinks + .filter( + (navLink): navLink is NavigationPageItem => navLink.kind === "page", + ) + .map((navLink) => ( + + + + + + + {navLink.icon} + {navLink.title} + + + + + + + ))} + + ); } diff --git a/src/components/RapidHandout.tsx b/src/components/RapidHandout.tsx deleted file mode 100644 index 53afb41..0000000 --- a/src/components/RapidHandout.tsx +++ /dev/null @@ -1,37 +0,0 @@ -"use client"; -import { UserDetail } from "@boklisten/bl-model"; -import { Alert, Button } from "@mui/material"; -import { useState } from "react"; - -import { isEmployee, isLoggedIn } from "@/api/auth"; -import DynamicLink from "@/components/DynamicLink"; -import RapidHandoutDetails from "@/components/RapidHandoutDetails"; -import UserDetailSearchField from "@/components/search/UserDetailSearchField"; -import useIsHydrated from "@/utils/useIsHydrated"; - -export default function RapidHandout() { - const hydrated = useIsHydrated(); - const [customer, setCustomer] = useState(null); - - return hydrated && isLoggedIn() && isEmployee() ? ( - <> - { - setCustomer(userDetail); - }} - /> - {customer && } - - ) : ( - <> - - Du må ha ansattilgang for å bruke denne siden. - - - - - - ); -} diff --git a/src/components/admin/communication/CustomerItemTypePicker.tsx b/src/components/admin/communication/CustomerItemTypePicker.tsx new file mode 100644 index 0000000..38abf8f --- /dev/null +++ b/src/components/admin/communication/CustomerItemTypePicker.tsx @@ -0,0 +1,32 @@ +import { CustomerItemType } from "@boklisten/bl-model"; +import { Box, Typography } from "@mui/material"; +import ToggleButton from "@mui/material/ToggleButton"; +import ToggleButtonGroup from "@mui/material/ToggleButtonGroup"; +import { useState } from "react"; + +export default function CustomerItemTypePicker({ + onChange, +}: { + onChange: (selectedCustomerItemType: CustomerItemType | null) => void; +}) { + const [customerItemType, setCustomerItemType] = useState(null); + return ( + + + Kundetype + + { + onChange(newCustomerItemType); + setCustomerItemType(newCustomerItemType); + }} + > + VGS + Privatist + + + ); +} diff --git a/src/components/admin/communication/DeadlinePicker.tsx b/src/components/admin/communication/DeadlinePicker.tsx new file mode 100644 index 0000000..9617aaf --- /dev/null +++ b/src/components/admin/communication/DeadlinePicker.tsx @@ -0,0 +1,93 @@ +import { Box, Grid2, Typography } from "@mui/material"; +import ToggleButton from "@mui/material/ToggleButton"; +import ToggleButtonGroup from "@mui/material/ToggleButtonGroup"; +import { DatePicker } from "@mui/x-date-pickers"; +import moment, { Moment } from "moment"; +import { useState } from "react"; + +function calculateDeadlineOptions() { + const today = new Date(); + const summerDeadline = new Date(today.getFullYear(), 6, 1); + const winterDeadline = new Date(today.getFullYear(), 11, 20); + // Continue to show the deadline for a month afterward, with warning + const summerDeadlinePlusGracePeriod = new Date( + summerDeadline.getFullYear(), + 7, + 1, + ); + const winterDeadlinePlusGracePeriod = new Date( + winterDeadline.getFullYear() + 1, + 0, + 20, + ); + + if (today > summerDeadlinePlusGracePeriod) { + summerDeadline.setFullYear(today.getFullYear() + 1); + } + if (today > winterDeadlinePlusGracePeriod) { + winterDeadline.setFullYear(today.getFullYear() + 1); + } + + let usualDates = [summerDeadline, winterDeadline]; + if (summerDeadline > winterDeadline) { + usualDates = [winterDeadline, summerDeadline]; + } + + return usualDates; +} + +export default function DeadlinePicker({ + onChange, +}: { + onChange: (selectedDeadline: Date | null) => void; +}) { + const [datePickerValue, setDatePickerValue] = useState(null); + const [deadlineToggleValue, setDeadlineToggleValue] = useState( + null, + ); + return ( + + + Frist + + + { + onChange( + newDeadline === "custom" + ? datePickerValue?.isValid() + ? datePickerValue.toDate() + : null + : newDeadline, + ); + setDeadlineToggleValue(newDeadline); + }} + > + {calculateDeadlineOptions().map((option) => ( + + {moment(option).format("DD/MM/yyyy")} + + ))} + Egendefinert + + {deadlineToggleValue === "custom" && ( + { + onChange(newDeadline?.isValid() ? newDeadline.toDate() : null); + setDatePickerValue(newDeadline); + }} + /> + )} + + + ); +} diff --git a/src/components/admin/communication/MessageMethodPicker.tsx b/src/components/admin/communication/MessageMethodPicker.tsx new file mode 100644 index 0000000..0ea5020 --- /dev/null +++ b/src/components/admin/communication/MessageMethodPicker.tsx @@ -0,0 +1,33 @@ +import { MessageMethod } from "@boklisten/bl-model/message/message-method/message-method"; +import { Box, Typography } from "@mui/material"; +import ToggleButton from "@mui/material/ToggleButton"; +import ToggleButtonGroup from "@mui/material/ToggleButtonGroup"; +import { useState } from "react"; + +export default function MessageMethodPicker({ + onChange, +}: { + onChange: (selectedMessageMethod: MessageMethod | null) => void; +}) { + const [messageMethod, setMessageMethod] = useState(null); + return ( + + + Meldingstype + + { + onChange(newMessageMethod); + setMessageMethod(newMessageMethod); + }} + > + SMS + E-post + + + ); +} diff --git a/src/components/admin/communication/MultiBranchPicker.tsx b/src/components/admin/communication/MultiBranchPicker.tsx new file mode 100644 index 0000000..cdb91ce --- /dev/null +++ b/src/components/admin/communication/MultiBranchPicker.tsx @@ -0,0 +1,119 @@ +import { Branch } from "@boklisten/bl-model"; +import { Button, Typography } from "@mui/material"; +import Box from "@mui/material/Box"; +import Checkbox from "@mui/material/Checkbox"; +import Chip from "@mui/material/Chip"; +import FormControl from "@mui/material/FormControl"; +import InputLabel from "@mui/material/InputLabel"; +import ListItemText from "@mui/material/ListItemText"; +import MenuItem from "@mui/material/MenuItem"; +import OutlinedInput from "@mui/material/OutlinedInput"; +import Select, { SelectChangeEvent } from "@mui/material/Select"; +import { useState } from "react"; +import useSWR from "swr"; + +import BlFetcher from "@/api/blFetcher"; +import BL_CONFIG from "@/utils/bl-config"; + +const ITEM_HEIGHT = 48; +const ITEM_PADDING_TOP = 8; +const MenuProps = { + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, + width: 250, + }, + }, +}; + +export default function MultiBranchPicker({ + onChange, +}: { + onChange: (newBranchIds: string[]) => void; +}) { + const [branchIDs, setBranchIDs] = useState([]); + + const { data: branches } = useSWR( + `${BL_CONFIG.collection.branch}?active=true&sort=name`, + BlFetcher.get, + ); + + const handleChange = (event: SelectChangeEvent) => { + const { + target: { value }, + } = event; + const newBranchIds = typeof value === "string" ? value.split(",") : value; + onChange(newBranchIds); + setBranchIDs(newBranchIds); + }; + + return ( + <> + + Skoler + + + Velg skoler + + + + ); +} diff --git a/src/utils/adminPageNavigation.tsx b/src/utils/adminNavigation.tsx similarity index 87% rename from src/utils/adminPageNavigation.tsx rename to src/utils/adminNavigation.tsx index 1737e6b..ea3507d 100644 --- a/src/utils/adminPageNavigation.tsx +++ b/src/utils/adminNavigation.tsx @@ -54,6 +54,21 @@ const EMPLOYEE_LINKS: Navigation = [ }, ]; +export const COMMUNICATION_SUB_PAGES: Navigation = [ + { + title: "Påminnelser", + icon: , + segment: "paaminnelser", + kind: "page", + }, + { + title: "Overleveringer", + icon: , + segment: "overleveringer", + kind: "page", + }, +]; + const ADMIN_LINKS: Navigation = [ { title: "Faktura", @@ -66,18 +81,7 @@ const ADMIN_LINKS: Navigation = [ icon: , segment: "admin/kommunikasjon", kind: "page", - children: [ - { - title: "Påminnelser", - icon: , - segment: "paaminnelser", - }, - { - title: "Overleveringer", - icon: , - segment: "overleveringer", - }, - ], + children: COMMUNICATION_SUB_PAGES, }, { title: "Databaseverktøy", @@ -119,6 +123,7 @@ const USER_LINKS: Navigation = [ { title: "Gå til Boklisten.no", icon: , + segment: "/", kind: "page", }, ];