diff --git a/package.json b/package.json index 9ccbc17..a188080 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "@nextui-org/date-picker": "^2.1.8", "@nextui-org/theme": "^2.2.11", + "@radix-ui/react-alert-dialog": "^1.1.2", "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-popover": "^1.1.2", "@radix-ui/react-progress": "^1.1.0", diff --git a/src/app/layout.tsx b/src/app/layout.tsx index ac1a395..d30fe05 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -31,8 +31,8 @@ export default function RootLayout({ }>) { return ( - - {children} + +
{children}
diff --git a/src/components/announcement/announcementForm.tsx b/src/components/announcement/announcementForm.tsx index 72cb27d..dd06f6a 100644 --- a/src/components/announcement/announcementForm.tsx +++ b/src/components/announcement/announcementForm.tsx @@ -1,9 +1,11 @@ -import CrossThinIcon from "@/icons/crossThinIcon"; -import SpeakerIcon from "@/icons/speakerIcon"; import { Announcement } from "@/models/announcement"; +import { DateFormat } from "@/models/enums/dateFormat"; import { useUserStore } from "@/stores/useUserStore"; +import { formatDate } from "@/utils/utils"; import { addDays } from "date-fns"; +import { Speaker, X } from "lucide-react"; import { useMemo, useState } from "react"; +import AlertPopup from "../system/alertPopup"; import ButtonPrimary1 from "../system/button/buttonPrimary1"; import errorToast from "../system/errorToast"; import InputDatePicker from "../system/input/inputDatePicker"; @@ -39,6 +41,8 @@ export default function AnnouncementForm({ onClose, editingAnnouncement }: Props const { user } = useUserStore(); const [announcement, setAnnouncement] = useState(editingAnnouncement ?? dummyAnnouncement); + const [showAlert, setShowAlert] = useState(false); + const handleChange = (field: keyof Announcement, value: any) => { setAnnouncement((prev) => ({ ...prev, [field]: value })); }; @@ -70,88 +74,103 @@ export default function AnnouncementForm({ onClose, editingAnnouncement }: Props }; return ( -
-
- {/* Header */} -
-
- -
-

Create Announcement

-

Schedule an announcement to our apps with this form.

+ <> + {/* Alert */} + setShowAlert(val)} + /> + +
+
+ {/* Header */} +
+
+ +
+

Create Announcement

+

Schedule an announcement to our apps with this form.

+
+
- -
-
- {/* Preview */} -
- -
+
+ {/* Preview */} +
+ +
- {/* Form */} -
- handleChange("title", event.target.value)} - /> - handleChange("body", event.target.value)} - /> -
-
Date
- { - handleChange("startDate", dateRange?.from ?? ""); - handleChange("endDate", dateRange?.to ?? ""); - }} + {/* Form */} +
+ handleChange("title", event.target.value)} /> -
- handleChange("link", event.target.value)} - /> -
-
Apps
- handleChange("apps", apps)} /> -
-
-
Upload Image
- handleChange("imageUrl", url)} /> -
+ handleChange("body", event.target.value)} + /> +
+
Date
+ { + handleChange("startDate", dateRange?.from ?? ""); + handleChange("endDate", dateRange?.to ?? ""); + }} + /> +
+ handleChange("link", event.target.value)} + /> +
+
Apps
+ handleChange("apps", apps)} /> +
+
+
Upload Image
+ handleChange("imageUrl", url)} /> +
- {/* Submit Button */} - + {/* Submit Button */} + +
-
- {/* Submit Button */} - + {/* Submit Button */} + setShowAlert(true)} + disabled={!canSchedule} + className="max-lg:hidden w-full" + /> +
-
+ ); } diff --git a/src/components/announcement/announcementModal.tsx b/src/components/announcement/announcementModal.tsx index e2bea1d..1945603 100644 --- a/src/components/announcement/announcementModal.tsx +++ b/src/components/announcement/announcementModal.tsx @@ -1,10 +1,14 @@ import AppIcon from "@/icons/appIcon"; -import CrossThinIcon from "@/icons/crossThinIcon"; import { Announcement } from "@/models/announcement"; import { DateFormat } from "@/models/enums/dateFormat"; +import { useUserStore } from "@/stores/useUserStore"; import { dateInRange, formatDate } from "@/utils/utils"; +import { X } from "lucide-react"; +import { useState } from "react"; +import AlertPopup from "../system/alertPopup"; import ButtonPrimary2 from "../system/button/buttonPrimary2"; import ButtonSecondary2 from "../system/button/buttonSecondary2"; +import errorToast from "../system/errorToast"; import AnnouncementBanner from "./announcementBanner"; import AnnouncementIndicator from "./announcementIndicator"; @@ -14,55 +18,101 @@ interface AnnouncementModalProps { } export default function AnnouncementModal({ onClose, announcement }: AnnouncementModalProps) { + const { user } = useUserStore(); + + const [showDeleteAlert, setShowDeleteAlert] = useState(false); + const [showEndAlert, setShowEndAlert] = useState(false); + if (!announcement) return null; + // End Announcement + const endAnnouncement = async () => { + if (!user) return; + + try { + console.log("Ending", announcement); + } catch (err) { + console.error(err); + errorToast(); + } + }; + + // Delete Announcement + const deleteAnnouncement = async () => { + if (!user) return; + + try { + console.log("Deleting", announcement); + } catch (err) { + console.error(err); + errorToast(); + } + }; + return ( -
-
-
-
-
-
-

{announcement.title}

- + <> + {/* Alerts */} + setShowEndAlert(val)} + /> + setShowDeleteAlert(val)} + /> + +
+
+
+
+
+
+

{announcement.title}

+ +
+

+ {`${formatDate(new Date(announcement.startDate), DateFormat.SHORT)} - ${formatDate( + new Date(announcement.endDate), + DateFormat.SHORT + )}`} +

+
+
+ +

{`Scheduled by ${announcement.creator.name}`}

-

- {`${formatDate(new Date(announcement.startDate), DateFormat.SHORT)} - ${formatDate( - new Date(announcement.endDate), - DateFormat.SHORT - )}`} -

-
- -

{`Scheduled by ${announcement.creator.name}`}

+
+
+ {announcement.apps.map((app) => ( + + ))} +
+
-
-
-
- {announcement.apps.map((app) => ( - - ))} + +
+
- -
-
- + {dateInRange(new Date(announcement.startDate), new Date(announcement.endDate), new Date()) ? ( + setShowEndAlert(true)} /> + ) : ( + setShowDeleteAlert(true)} /> + )}
- - {dateInRange(new Date(announcement.startDate), new Date(announcement.endDate), new Date()) ? ( - console.log("End Live Announcement button tapped")} - /> - ) : ( - console.log("Delete button tapped")} /> - )}
-
+ ); } diff --git a/src/components/past/past.tsx b/src/components/past/past.tsx index 0a8c98a..79de14c 100644 --- a/src/components/past/past.tsx +++ b/src/components/past/past.tsx @@ -1,21 +1,21 @@ "use client"; +import FilterIcon from "@/icons/filterIcon"; import { Announcement } from "@/models/announcement"; import { AppName } from "@/models/enums/appName"; import { SortType } from "@/models/enums/sortType"; import { createDummyAnnouncement } from "@/utils/dummy"; +import { dateInRange } from "@/utils/utils"; import { ChangeEvent, useEffect, useState } from "react"; +import { DateRange } from "react-day-picker"; +import Footer from "../common/footer"; import NavBar from "../common/navBar"; import PageHeader from "../common/pageHeader"; -import InputSearch from "../system/input/inputSearch"; import Divider from "../system/divider"; -import PastAnnouncementCell from "./pastAnnouncementCell"; -import Footer from "../common/footer"; +import InputSearch from "../system/input/inputSearch"; import { InputSelect } from "../system/input/inputSelect"; -import FilterIcon from "@/icons/filterIcon"; +import PastAnnouncementCell from "./pastAnnouncementCell"; import PastFilter from "./pastFilter"; -import { DateRange } from "react-day-picker"; -import { dateInRange } from "@/utils/utils"; // TODO: Replace with React Query to fetch from API const allAnnouncements: Announcement[] = [ diff --git a/src/components/system/alertPopup.tsx b/src/components/system/alertPopup.tsx new file mode 100644 index 0000000..50ef06b --- /dev/null +++ b/src/components/system/alertPopup.tsx @@ -0,0 +1,43 @@ +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "../ui/alert-dialog"; + +interface Props { + title: string; + description: string; + actionText?: string; + action: () => void; + open: boolean; + onOpenChange: (val: boolean) => void; +} + +export default function AlertPopup({ title, description, actionText = "Continue", action, open, onOpenChange }: Props) { + return ( + + + + {title} + {description} + + + + Cancel + + + {actionText} + + + + + ); +} diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..72241f1 --- /dev/null +++ b/src/components/ui/alert-dialog.tsx @@ -0,0 +1,102 @@ +"use client"; + +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; +import * as React from "react"; + +import { buttonVariants } from "@/components/ui/button"; +import { cn } from "@/lib/utils"; + +const AlertDialog = AlertDialogPrimitive.Root; + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger; + +const AlertDialogPortal = AlertDialogPrimitive.Portal; + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)); +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; + +const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +AlertDialogHeader.displayName = "AlertDialogHeader"; + +const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +AlertDialogFooter.displayName = "AlertDialogFooter"; + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName; + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; + +export { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogOverlay, + AlertDialogPortal, + AlertDialogTitle, + AlertDialogTrigger, +}; diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index ec82d9f..1601b5e 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,6 +1,6 @@ -import * as React from "react"; import { Slot } from "@radix-ui/react-slot"; import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react"; import { cn } from "@/lib/utils"; diff --git a/yarn.lock b/yarn.lock index b5c8a6d..975b81f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1099,6 +1099,18 @@ resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.0.tgz#42ef83b3b56dccad5d703ae8c42919a68798bbe2" integrity sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA== +"@radix-ui/react-alert-dialog@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.2.tgz#ac3bb7f71f5cbb595d3d0949bb12b598c2a99981" + integrity sha512-eGSlLzPhKO+TErxkiGcCZGuvbVMnLA1MTnyBksGOeGRGkxHiiJUujsjmNTdWTm4iHVSRaUao9/4Ur671auMghQ== + dependencies: + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-dialog" "1.1.2" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-slot" "1.1.0" + "@radix-ui/react-arrow@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz#744f388182d360b86285217e43b6c63633f39e7a" @@ -1166,7 +1178,7 @@ aria-hidden "^1.1.1" react-remove-scroll "2.5.5" -"@radix-ui/react-dialog@^1.1.2": +"@radix-ui/react-dialog@1.1.2", "@radix-ui/react-dialog@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.2.tgz#d9345575211d6f2d13e209e84aec9a8584b54d6c" integrity sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==