feat: add delete confirmation screens

This commit is contained in:
swve 2023-07-25 20:39:09 +02:00
parent acc73f018e
commit 631f927853
4 changed files with 166 additions and 18 deletions

View file

@ -2,6 +2,7 @@
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'; import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement';
import { AuthContext } from '@components/Security/AuthProvider'; import { AuthContext } from '@components/Security/AuthProvider';
import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal';
import { getUriWithOrg } from '@services/config/config'; import { getUriWithOrg } from '@services/config/config';
import { deleteCollection } from '@services/courses/collections'; import { deleteCollection } from '@services/courses/collections';
import { revalidateTags } from '@services/utils/ts/requests'; import { revalidateTags } from '@services/utils/ts/requests';
@ -26,9 +27,17 @@ const CollectionAdminEditsArea = (props: any) => {
return ( return (
<AuthenticatedClientElement orgId={props.org_id} checkMethod='roles'> <AuthenticatedClientElement orgId={props.org_id} checkMethod='roles'>
<div className="flex space-x-2 py-2"> <div className="flex space-x-2 py-2">
<button className="rounded-md text-sm px-3 font-bold text-red-800 bg-red-200 w-16 flex justify-center items-center" onClick={() => deleteCollectionUI(props.collection_id)}> <ConfirmationModal
confirmationMessage="Are you sure you want to delete this collection?"
confirmationButtonText="Delete Collection"
dialogTitle={"Delete " + props.collection.name + " ?"}
dialogTrigger={
<button className="rounded-md text-sm px-3 font-bold text-red-800 bg-red-200 w-16 flex justify-center items-center" >
Delete <Trash size={10}></Trash> Delete <Trash size={10}></Trash>
</button> </button>}
functionToExecute={() => deleteCollectionUI(props.collection_id)}
status='warning'
></ConfirmationModal>
</div> </div>
</AuthenticatedClientElement> </AuthenticatedClientElement>
) )

View file

@ -16,6 +16,7 @@ import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWra
import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentTitle'; import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentTitle';
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'; import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement';
import { getCourseThumbnailMediaDirectory } from '@services/media/media'; import { getCourseThumbnailMediaDirectory } from '@services/media/media';
import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal';
interface CourseProps { interface CourseProps {
orgslug: string; orgslug: string;
@ -48,6 +49,7 @@ function Courses(props: CourseProps) {
return ( return (
<div> <div>
<GeneralWrapperStyled> <GeneralWrapperStyled>
<div className='flex flex-wrap justify-between'> <div className='flex flex-wrap justify-between'>
<TypeOfContentTitle title="Courses" type="cou" /> <TypeOfContentTitle title="Courses" type="cou" />
<AuthenticatedClientElement checkMethod='roles' orgId={props.org_id}> <AuthenticatedClientElement checkMethod='roles' orgId={props.org_id}>
@ -94,9 +96,17 @@ function Courses(props: CourseProps) {
const AdminEditsArea = (props: { orgSlug: string, courseId: string, course: any, deleteCourses: any }) => { const AdminEditsArea = (props: { orgSlug: string, courseId: string, course: any, deleteCourses: any }) => {
return ( return (
<AuthenticatedClientElement checkMethod='roles' orgId={props.course.org_id}><div className="flex space-x-2 py-2"> <AuthenticatedClientElement checkMethod='roles' orgId={props.course.org_id}><div className="flex space-x-2 py-2">
<button className="rounded-md text-sm px-3 font-bold text-red-800 bg-red-200 w-16 flex justify-center items-center" onClick={() => props.deleteCourses(props.courseId)}> <ConfirmationModal
confirmationButtonText='Delete Course'
confirmationMessage='Are you sure you want to delete this course?'
dialogTitle={'Delete ' + props.course.name + ' ?'}
dialogTrigger={
<button className="rounded-md text-sm px-3 font-bold text-red-800 bg-red-200 w-16 flex justify-center items-center" >
Delete <Trash size={10}></Trash> Delete <Trash size={10}></Trash>
</button> </button>}
functionToExecute={() => props.deleteCourses(props.courseId)}
status='warning'
></ConfirmationModal>
<Link href={getUriWithOrg(props.orgSlug, "/course/" + removeCoursePrefix(props.courseId) + "/edit")}> <Link href={getUriWithOrg(props.orgSlug, "/course/" + removeCoursePrefix(props.courseId) + "/edit")}>
<button className="rounded-md text-sm px-3 font-bold text-orange-800 bg-orange-200 w-16 flex justify-center items-center"> <button className="rounded-md text-sm px-3 font-bold text-orange-800 bg-orange-200 w-16 flex justify-center items-center">
Edit <Edit2 size={10}></Edit2> Edit <Edit2 size={10}></Edit2>

View file

@ -3,6 +3,7 @@ import styled from "styled-components";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"; import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
import Activity from "./Activity"; import Activity from "./Activity";
import { PlusSquare, Trash, Trash2 } from "lucide-react"; import { PlusSquare, Trash, Trash2 } from "lucide-react";
import ConfirmationModal from "@components/StyledElements/ConfirmationModal/ConfirmationModal";
function Chapter(props: any) { function Chapter(props: any) {
return ( return (
@ -19,16 +20,18 @@ function Chapter(props: any) {
<h3 className="flex space-x-2 pt-3 font-bold text-md items-center"> <h3 className="flex space-x-2 pt-3 font-bold text-md items-center">
<p>{props.info.list.chapter.name} <p>{props.info.list.chapter.name}
</p> </p>
<ConfirmationModal
confirmationButtonText="Delete Chapter"
confirmationMessage="Are you sure you want to delete this chapter?"
dialogTitle={"Delete " + props.info.list.chapter.name + " ?"}
dialogTrigger={
<button className="rounded-md text-sm px-3 font-bold text-red-800 bg-red-200 w-16 flex justify-center items-center" >
Delete <Trash size={10}></Trash>
</button>}
functionToExecute={() => props.deleteChapter(props.info.list.chapter.id)}
status='warning'
></ConfirmationModal>
<div
className="hover:cursor-pointer p-1 rounded-md bg-slate-100"
onClick={() => {
props.deleteChapter(props.info.list.chapter.id);
}}
>
<Trash2 className="text-slate-500" size={16} />
</div>
</h3> </h3>
<Droppable key={props.info.list.chapter.id} droppableId={props.info.list.chapter.id} type="activity"> <Droppable key={props.info.list.chapter.id} droppableId={props.info.list.chapter.id} type="activity">
{(provided) => ( {(provided) => (
@ -68,8 +71,6 @@ const ChapterWrapper = styled.div`
border: 1px solid rgba(255, 255, 255, 0.19); border: 1px solid rgba(255, 255, 255, 0.19);
box-shadow: 0px 13px 33px -13px rgb(0 0 0 / 12%); box-shadow: 0px 13px 33px -13px rgb(0 0 0 / 12%);
transition: all 0.2s ease; transition: all 0.2s ease;
h3{ h3{
padding-left: 20px; padding-left: 20px;
padding-right: 20px; padding-right: 20px;

View file

@ -0,0 +1,128 @@
'use client';
import React from 'react';
import * as Dialog from '@radix-ui/react-dialog';
import { styled, keyframes } from '@stitches/react';
import { blackA, } from '@radix-ui/colors';
import { AlertTriangle, Info } from 'lucide-react';
type ModalParams = {
confirmationMessage: string;
confirmationButtonText: string;
dialogTitle: string;
functionToExecute: any;
dialogTrigger?: React.ReactNode;
status?: "warning" | "info";
};
const ConfirmationModal = (params: ModalParams) => {
const [isDialogOpen, setIsDialogOpen] = React.useState(false);
const warningColors = 'bg-red-100 text-red-600'
const infoColors = 'bg-blue-100 text-blue-600'
const warningButtonColors = 'text-white bg-red-500 hover:bg-red-600'
const infoButtonColors = 'text-white bg-blue-500 hover:bg-blue-600'
const onOpenChange = React.useCallback(
(open: any) => {
setIsDialogOpen(open);
},
[setIsDialogOpen]
);
return (
<Dialog.Root open={isDialogOpen} onOpenChange={onOpenChange}>
{params.dialogTrigger ? (
<Dialog.Trigger asChild >
{params.dialogTrigger}
</Dialog.Trigger>
) : null}
<Dialog.Portal>
<DialogOverlay />
<DialogContent >
<div className='h-26 flex space-x-4 tracking-tight'>
<div className={`icon p-6 rounded-xl flex items-center align-content-center ${params.status === 'warning' ? warningColors : infoColors}`}>
{params.status === 'warning' ? <AlertTriangle size={35} /> : <Info size={35} />}
</div>
<div className="text pt-1 space-x-0 w-auto flex-grow">
<div className="text-xl font-bold text-black ">
{params.dialogTitle}
</div>
<div className="text-md text-gray-500 w-60 leading-tight">
{params.confirmationMessage}
</div>
<div className="flex flex-row-reverse pt-2">
<div className={`rounded-md text-sm px-3 py-2 font-bold flex justify-center items-center hover:cursor-pointer ${params.status === 'warning' ? warningButtonColors : infoButtonColors}
hover:shadow-lg transition duration-300 ease-in-out
`}
onClick={() => { params.functionToExecute(); setIsDialogOpen(false) }}>
{params.confirmationButtonText}
</div>
</div>
</div>
</div>
</DialogContent>
</Dialog.Portal>
</Dialog.Root>
)
};
const overlayShow = keyframes({
'0%': { opacity: 0 },
'100%': { opacity: 1 },
});
const overlayClose = keyframes({
'0%': { opacity: 1 },
'100%': { opacity: 0 },
});
const contentShow = keyframes({
'0%': { opacity: 0, transform: 'translate(-50%, -50%) scale(.96)' },
'100%': { opacity: 1, transform: 'translate(-50%, -50%) scale(1)' },
});
const contentClose = keyframes({
'0%': { opacity: 1, transform: 'translate(-50%, -50%) scale(1)' },
'100%': { opacity: 0, transform: 'translate(-50%, -52%) scale(.96)' },
});
const DialogOverlay = styled(Dialog.Overlay, {
backgroundColor: blackA.blackA9,
position: 'fixed',
zIndex: 500,
inset: 0,
animation: `${overlayShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
'&[data-state="closed"]': {
animation: `${overlayClose} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
},
});
const DialogContent = styled(Dialog.Content, {
backgroundColor: 'white',
borderRadius: 18,
zIndex: 501,
boxShadow: 'hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px',
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 'auto',
minWidth: '500px',
overflow: 'hidden',
height: 'auto',
maxHeight: '85vh',
maxWidth: '600px',
padding: 11,
animation: `${contentShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
'&:focus': { outline: 'none' },
'&[data-state="closed"]': {
animation: `${contentClose} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
},
transition: "max-height 0.3s ease-out",
});
export default ConfirmationModal;