mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: add delete confirmation screens
This commit is contained in:
parent
acc73f018e
commit
631f927853
4 changed files with 166 additions and 18 deletions
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement';
|
||||
import { AuthContext } from '@components/Security/AuthProvider';
|
||||
import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal';
|
||||
import { getUriWithOrg } from '@services/config/config';
|
||||
import { deleteCollection } from '@services/courses/collections';
|
||||
import { revalidateTags } from '@services/utils/ts/requests';
|
||||
|
|
@ -26,9 +27,17 @@ const CollectionAdminEditsArea = (props: any) => {
|
|||
return (
|
||||
<AuthenticatedClientElement orgId={props.org_id} checkMethod='roles'>
|
||||
<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)}>
|
||||
Delete <Trash size={10}></Trash>
|
||||
</button>
|
||||
<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>
|
||||
</button>}
|
||||
functionToExecute={() => deleteCollectionUI(props.collection_id)}
|
||||
status='warning'
|
||||
></ConfirmationModal>
|
||||
</div>
|
||||
</AuthenticatedClientElement>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWra
|
|||
import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentTitle';
|
||||
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement';
|
||||
import { getCourseThumbnailMediaDirectory } from '@services/media/media';
|
||||
import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal';
|
||||
|
||||
interface CourseProps {
|
||||
orgslug: string;
|
||||
|
|
@ -48,6 +49,7 @@ function Courses(props: CourseProps) {
|
|||
return (
|
||||
<div>
|
||||
<GeneralWrapperStyled>
|
||||
|
||||
<div className='flex flex-wrap justify-between'>
|
||||
<TypeOfContentTitle title="Courses" type="cou" />
|
||||
<AuthenticatedClientElement checkMethod='roles' orgId={props.org_id}>
|
||||
|
|
@ -73,7 +75,7 @@ function Courses(props: CourseProps) {
|
|||
<div className="flex flex-wrap">
|
||||
{courses.map((course: any) => (
|
||||
<div className="px-3" key={course.course_id}>
|
||||
<AdminEditsArea course={course} orgSlug={orgslug} courseId={course.course_id} deleteCourses={deleteCourses} />
|
||||
<AdminEditsArea course={course} orgSlug={orgslug} courseId={course.course_id} deleteCourses={deleteCourses} />
|
||||
<Link href={getUriWithOrg(orgslug, "/course/" + removeCoursePrefix(course.course_id))}>
|
||||
<div className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-[249px] h-[131px] bg-cover" style={{ backgroundImage: `url(${getCourseThumbnailMediaDirectory(course.org_id, course.course_id, course.thumbnail)})` }}>
|
||||
|
||||
|
|
@ -94,9 +96,17 @@ function Courses(props: CourseProps) {
|
|||
const AdminEditsArea = (props: { orgSlug: string, courseId: string, course: any, deleteCourses: any }) => {
|
||||
return (
|
||||
<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)}>
|
||||
Delete <Trash size={10}></Trash>
|
||||
</button>
|
||||
<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>
|
||||
</button>}
|
||||
functionToExecute={() => props.deleteCourses(props.courseId)}
|
||||
status='warning'
|
||||
></ConfirmationModal>
|
||||
<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">
|
||||
Edit <Edit2 size={10}></Edit2>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import styled from "styled-components";
|
|||
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
|
||||
import Activity from "./Activity";
|
||||
import { PlusSquare, Trash, Trash2 } from "lucide-react";
|
||||
import ConfirmationModal from "@components/StyledElements/ConfirmationModal/ConfirmationModal";
|
||||
|
||||
function Chapter(props: any) {
|
||||
return (
|
||||
|
|
@ -19,16 +20,18 @@ function Chapter(props: any) {
|
|||
<h3 className="flex space-x-2 pt-3 font-bold text-md items-center">
|
||||
<p>{props.info.list.chapter.name}
|
||||
</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>
|
||||
<Droppable key={props.info.list.chapter.id} droppableId={props.info.list.chapter.id} type="activity">
|
||||
{(provided) => (
|
||||
|
|
@ -68,8 +71,6 @@ const ChapterWrapper = styled.div`
|
|||
border: 1px solid rgba(255, 255, 255, 0.19);
|
||||
box-shadow: 0px 13px 33px -13px rgb(0 0 0 / 12%);
|
||||
transition: all 0.2s ease;
|
||||
|
||||
|
||||
h3{
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
Loading…
Add table
Add a link
Reference in a new issue