Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2fdf9d7
feat(frontend): implement question type components (#540)
minhle35 Apr 14, 2025
8008752
fix(): use React DnD for ranking options
minhle35 May 1, 2025
07dbc3d
fix(dropdown): add borderline, improve hover effects, and highlight s…
minhle35 May 1, 2025
7955980
fix(shortanswer): enlarge textarea to span 77 columns and 3 rows
minhle35 May 1, 2025
e583f67
chore(deps): update yarn.lock with automatic modifications
minhle35 May 1, 2025
5b65667
feat(shortAnswer): enhance textarea focus visibility and border styling
minhle35 Jun 6, 2025
b025cdb
refactor: update question components structure and remove unneccsary …
minhle35 Jun 6, 2025
763c71b
setup shadcn components
gyoumi Jun 9, 2025
1ac3172
Feat/shad cn UI question components (#559)
minhle35 Jun 20, 2025
09eaf31
Chaos 551 feature/interview-booking-component (#565)
peternuyn Aug 1, 2025
ae12cd8
feat: Implement application review page for recruitment form (#563)
minhle35 Aug 1, 2025
138eb2f
commiting lock file
gyoumi Aug 1, 2025
78704a2
email page initialisation
remyjelee Aug 1, 2025
bdc993e
Merge branch 'CHAOS-554-FE-v0-setup' of github.com:devsoc-unsw/chaos …
remyjelee Aug 1, 2025
5e6aa0f
Made email template functional and fixed obvious flaws (Duplicate tex…
remyjelee Aug 1, 2025
8d873a6
Finished email templates and editor page
remyjelee Aug 20, 2025
321f95d
Merge branch 'CHAOS-571-integrate-be-fe' into CHAOS-552-email-templates
gyoumi Aug 27, 2025
ca076fd
fixed accidental dupe line while merging
gyoumi Aug 27, 2025
7b8045c
edited according to isaacs recommendations
remyjelee Sep 24, 2025
cc915e6
merged from remote
remyjelee Sep 24, 2025
95c9c11
Fixed left leaning issue
remyjelee Sep 24, 2025
e9edad6
Merged from remote 571
remyjelee Oct 7, 2025
e3ef721
Split email-templates into smaller components
remyjelee Oct 7, 2025
4781792
removed redundant lock files
remyjelee Oct 9, 2025
823dd9e
Resolved merge conflicts in routes.tsx
remyjelee Oct 9, 2025
99000ba
added yarn install lock (latest)
remyjelee Oct 9, 2025
a8eee6b
deleted useless files on root dir
remyjelee Oct 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/src/components/ui/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ const SelectItem = React.forwardRef<
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
"relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none hover:bg-gray-100 hover:text-gray-900 focus:bg-accent focus:text-accent-foreground data-[highlighted]:bg-gray-100 data-[highlighted]:text-gray-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import Button from "@/components/Button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { EyeIcon, PencilIcon, DocumentDuplicateIcon, TrashIcon } from "@heroicons/react/24/outline";
import { templateCategories } from "../../constants";
import type { EmailTemplate } from "../../types";


interface EmailTemplateCardProps {
template: EmailTemplate;
onPreview: (template: EmailTemplate) => void;
onEdit: (template: EmailTemplate) => void;
onDuplicate: (template: EmailTemplate) => void;
onDelete: (templateId: number) => void;
}


const EmailTemplateCard = ({
template,
onPreview,
onEdit,
onDuplicate,
onDelete,
}: EmailTemplateCardProps) => {
const getCategoryBadgeColor = (category: string) => {
switch (category) {
case "interview":
return "bg-blue-100 text-blue-800";
case "acceptance":
return "bg-green-100 text-green-800";
case "rejection":
return "bg-red-100 text-red-800";
case "confirmation":
return "bg-purple-100 text-purple-800";
case "reminder":
return "bg-orange-100 text-orange-800";
default:
return "bg-gray-100 text-gray-800";
}
};


return (
<Card key={template.id} className="hover:shadow-md transition-shadow flex flex-col h-full">
<CardHeader className="pb-3">
<div className="flex items-start justify-between">
<div className="flex-1">
<CardTitle className="text-lg">{template.name}</CardTitle>
<Badge className={`mt-2 ${getCategoryBadgeColor(template.category)}`}>
{templateCategories.find((c) => c.value === template.category)?.label}
</Badge>
</div>
</div>
</CardHeader>
<CardContent className="flex flex-col flex-1">
<div className="space-y-3 flex-1">
<div>
<p className="text-sm font-medium text-gray-700">Subject:</p>
<p className="text-sm text-gray-600 line-clamp-2">{template.subject}</p>
</div>
<div>
<p className="text-sm font-medium text-gray-700">Preview:</p>
<p className="text-sm text-gray-600 line-clamp-3">{template.body}</p>
</div>
</div>


<div className="mt-auto">
<div className="text-xs text-gray-500 mb-4">
<p>Created: {new Date(template.createdAt).toLocaleDateString()}</p>
<p>Updated: {new Date(template.updatedAt).toLocaleDateString()}</p>
</div>


<div className="flex justify-between items-center gap-2">
<div className="flex gap-2">
<Button color="white" onClick={() => onPreview(template)} className="w-24 text-xs">
<EyeIcon className="w-4 h-4 mr-1 flex-shrink-0" />
<span>Preview</span>
</Button>
<Button color="white" onClick={() => onEdit(template)} className="w-20 text-xs">
<PencilIcon className="w-4 h-4 mr-1 flex-shrink-0" />
<span>Edit</span>
</Button>
<Button color="white" onClick={() => onDuplicate(template)} className="w-20 text-xs">
<DocumentDuplicateIcon className="w-4 h-4 mr-1 flex-shrink-0" />
<span>Copy</span>
</Button>
</div>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
color="danger"
className="flex-shrink-0 bg-red-600 text-white hover:bg-red-700"
aria-label="Delete template"
>
<TrashIcon className="w-4 h-4" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete template?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete the email template
"{template.name}".
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={() => onDelete(template.id)}>
Delete
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</div>
</CardContent>
</Card>
);
};


export default EmailTemplateCard;
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import Button from "@/components/Button";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { templateCategories } from "../../constants";

interface EmailTemplateFiltersProps {
searchTerm: string;
setSearchTerm: (value: string) => void;
selectedCategory: string;
setSelectedCategory: (value: string) => void;
}

const EmailTemplateFilters = ({
searchTerm,
setSearchTerm,
selectedCategory,
setSelectedCategory,
}: EmailTemplateFiltersProps) => {
const handleClearFilters = () => {
setSelectedCategory("all");
setSearchTerm("");
};

return (
<Card className="mb-8">
<CardContent className="p-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<Label htmlFor="search">Search Templates</Label>
<Input
id="search"
placeholder="Search by name or subject..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<div>
<Label htmlFor="category">Filter by Category</Label>
<Select value={selectedCategory} onValueChange={setSelectedCategory}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{templateCategories.map((category) => (
<SelectItem
key={category.value}
value={category.value}
className="cursor-pointer hover:bg-accent hover:text-accent-foreground"
>
{category.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex items-end">
<Button
color="white"
onClick={handleClearFilters}
className="w-full"
>
Clear Filters
</Button>
</div>
</div>
</CardContent>
</Card>
);
};

export default EmailTemplateFilters;
Loading