Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/toogle update toggle delete #541

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
"seed": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' prisma/seed.ts"
},
"dependencies": {
"100xdevs-job-board": "file:",
"@aws-sdk/client-s3": "^3.645.0",
"@aws-sdk/s3-request-presigner": "^3.645.0",
"@emotion/react": "^11.13.3",
Expand Down Expand Up @@ -59,7 +58,6 @@
"@types/lodash": "^4.17.7",
"@types/uuid": "^10.0.0",
"@uidotdev/usehooks": "^2.4.1",
"100xdevs-job-board": "file:",
"bcryptjs": "^2.4.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
Expand Down Expand Up @@ -102,6 +100,7 @@
"@types/react-dom": "^18",
"@typescript-eslint/eslint-plugin": "^8.1.0",
"@typescript-eslint/parser": "^8.1.0",
"100xdevs-job-board": "file:",
"eslint": "^8",
"eslint-config-next": "14.2.5",
"husky": "^9.1.4",
Expand Down
18 changes: 9 additions & 9 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ model User {
oauthId String?

blockedByAdmin DateTime?
onBoard Boolean @default(false)
bookmark Bookmark[]
onBoard Boolean @default(false)
bookmark Bookmark[]
}

enum OauthProvider {
Expand Down Expand Up @@ -80,21 +80,21 @@ model Job {
maxExperience Int?
isVerifiedJob Boolean @default(false) @map("is_verified_job")
deleted Boolean @default(false)
deletedAt DateTime?
postedAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
bookmark Bookmark[]
bookmark Bookmark[]
}

model Bookmark {
id String @id @default(uuid())
jobId String
userId String
job Job @relation(fields: [jobId],references: [id],onDelete: Cascade)
user User @relation(fields: [userId],references: [id],onDelete: Cascade)
id String @id @default(uuid())
jobId String
userId String
job Job @relation(fields: [jobId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}


model Experience {
id Int @id @default(autoincrement())
companyName String
Expand Down
3 changes: 2 additions & 1 deletion src/actions/corn.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// lib/cron.ts
import cron from 'node-cron';
import { updateExpiredJobs } from './job.action';
import { deleteOldDeltedJobs, updateExpiredJobs } from './job.action';

let cronJobInitialized = false;

Expand All @@ -12,6 +12,7 @@ export const startCronJob = () => {
cron.schedule('0 0 * * *', async () => {
try {
await updateExpiredJobs();
await deleteOldDeltedJobs();
} catch (error) {
console.error('Error updating expired jobs:', error);
}
Expand Down
66 changes: 58 additions & 8 deletions src/actions/job.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,47 +402,85 @@ export const updateJob = withServerActionAsyncCatcher<
).serialize();
});

export const deleteJobById = withServerActionAsyncCatcher<
export const toggleDeleteJobById = withServerActionAsyncCatcher<
DeleteJobByIdSchemaType,
ServerActionReturnType<deletedJob>
>(async (data) => {
const result = deleteJobByIdSchema.parse(data);
const { id } = result;
const deletedJob = await prisma.job.update({

// Fetch the current job's deleted status
const job = await prisma.job.findUnique({
where: {
id: id,
},
data: {
select: {
deleted: true,
deletedAt: true,
},
});

if (!job) {
throw new Error('Job not found');
}

const isNowDeleted = !job.deleted;
const deletedAt = isNowDeleted ? new Date() : null;

const updatedJob = await prisma.job.update({
where: {
id: id,
},
data: {
deleted: isNowDeleted,
deletedAt: deletedAt,
},
});
const deletedJobID = deletedJob.id;

const action = updatedJob.deleted ? 'Deleted' : 'Undeleted';
const deletedJobID = updatedJob.id;

revalidatePath('/manage');
return new SuccessResponse('Job Deleted successfully', 200, {

return new SuccessResponse(`Job ${action} successfully`, 200, {
deletedJobID,
}).serialize();
});

export const approveJob = withAdminServerAction<
export const toggleApproveJob = withAdminServerAction<
ApproveJobSchemaType,
ServerActionReturnType<ApprovedJobID>
>(async (session, data) => {
const result = ApproveJobSchema.safeParse(data);
if (!result.success) {
throw new Error(result.error.errors.toLocaleString());
}

const { id } = result.data;

const job = await prisma.job.findUnique({
where: { id: id },
select: { isVerifiedJob: true },
});

if (!job) {
throw new Error('Job not found');
}

await prisma.job.update({
where: {
id: id,
},
data: {
isVerifiedJob: true,
isVerifiedJob: !job.isVerifiedJob,
},
});

revalidatePath('/manage');
return new SuccessResponse('Job Approved', 200, { jobId: id }).serialize();
const message = job.isVerifiedJob ? 'Job Unapproved' : 'Job Approved';
return new SuccessResponse(message, 200, { jobId: id }).serialize();
});

export async function updateExpiredJobs() {
const currentDate = new Date();

Expand All @@ -458,6 +496,18 @@ export async function updateExpiredJobs() {
},
});
}
export const deleteOldDeltedJobs = async () => {
const twoWeeksAgo = new Date(Date.now() - 14 * 24 * 60 * 60 * 1000);

await prisma.job.deleteMany({
where: {
deleted: true,
deletedAt: {
lte: twoWeeksAgo,
},
},
});
};

export async function toggleBookmarkAction(userId: string, jobId: string) {
try {
Expand Down
86 changes: 86 additions & 0 deletions src/components/DeleteDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
'use client';
import React, { useState } from 'react';
import { Button } from './ui/button';
import { useToast } from './ui/use-toast';
import { toggleDeleteJobById } from '@/actions/job.action';
import { JobType } from '@/types/jobs.types';
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
} from './ui/dialog';
import { ArchiveRestore, Trash } from 'lucide-react';

const JobDialog = ({ job }: { job: JobType }) => {
const [dialogOpen, setDialogOpen] = useState(false); // State to manage dialog visibility
const { toast } = useToast();

const handelToggle = async () => {
try {
const result = await toggleDeleteJobById({ id: job.id });
toast({
title: result.message,
variant: 'default',
});
setDialogOpen(false);
} catch (error) {
console.error(error);
toast({ title: 'An Error occurred', variant: 'destructive' });
}
};

return (
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
<DialogTrigger>
{job.deleted ? (
<span
className="mr-5"
role="button"
onClick={() => setDialogOpen(true)}
>
<ArchiveRestore /> {/* Icon for restoring the job */}
</span>
) : (
<span
className="mr-5"
role="button"
onClick={() => setDialogOpen(true)}
>
<Trash /> {/* Icon for deleting the job */}
</span>
)}
</DialogTrigger>

<DialogContent>
<DialogHeader>
<DialogTitle>
{job.deleted
? 'Are you sure you want to Restore?'
: 'Are you absolutely sure?'}
</DialogTitle>
<DialogDescription>
{job.deleted
? 'This action will restore the deleted job.'
: 'This action cannot be undone. This will delete the selected job.'}
</DialogDescription>
</DialogHeader>

<DialogFooter>
<Button
className="mt-2"
variant={job.deleted ? 'secondary' : 'destructive'}
onClick={handelToggle}
>
{job.deleted ? 'Restore' : 'Delete'}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};

export default JobDialog;
50 changes: 10 additions & 40 deletions src/components/JobManagementTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,12 @@ import {
TableHeader,
TableRow,
} from './ui/table';
import { Edit, X } from 'lucide-react';
import { Edit } from 'lucide-react';
import { Badge } from '@/components/ui/badge';

import { getAllJobsAdditonalType } from '@/types/jobs.types';
import { ServerActionReturnType } from '@/types/api.types';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';

import { JobQuerySchemaType } from '@/lib/validators/jobs.validator';
import { DEFAULT_PAGE, JOBS_PER_PAGE } from '@/config/app.config';
import { Pagination, PaginationContent, PaginationItem } from './ui/pagination';
Expand All @@ -27,8 +21,8 @@ import {
} from './pagination-client';
import APP_PATHS from '@/config/path.config';
import { PaginationPages } from './ui/paginator';
import ApproveJobButton from './approveJobButton';
import RemoveJobButton from './removeJobButton';
import DeleteDialog from './DeleteDialog';
import ToggleApproveJobButton from './ToggleApproveJobButton';

type props = {
searchParams: JobQuerySchemaType;
Expand Down Expand Up @@ -64,42 +58,18 @@ const JobManagementTable = ({ jobs, searchParams }: props) => {
<TableCell>{job?.workMode}</TableCell>
<TableCell>{job?.city}</TableCell>
<TableCell>
{job.isVerifiedJob ? (
job.deleted ? (
<span className="bg-red-400 p-1 rounded-md text-secondary px-3 tracking-wide">
Deleted
</span>
) : (
<span className="bg-green-400 p-1 rounded-md text-secondary px-3 tracking-wide">
Approved
</span>
)
{job.deleted ? (
<Badge className="bg-red-600 text-white">Deleted</Badge>
) : (
<ApproveJobButton jobId={job.id} />
<ToggleApproveJobButton job={job} />
)}
</TableCell>
<TableCell className="text-right w-full flex justify-end">
<span className="mr-5" role="button">
<Edit />
</span>
<span role="button">
<Dialog>
<DialogTrigger>
<X />
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you absolutely sure?</DialogTitle>
<DialogDescription>
This action cannot be undone. This will Delete the
Selected JOB.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<RemoveJobButton jobId={job.id} />
</DialogFooter>
</DialogContent>
</Dialog>
<DeleteDialog job={job} />
</span>
</TableCell>
</TableRow>
Expand Down
Loading
Loading