Skip to content

Commit

Permalink
Merge branch 'main' into feat/admin-add-hr
Browse files Browse the repository at this point in the history
  • Loading branch information
devsharmagit authored Oct 23, 2024
2 parents aa9986b + e48bbef commit 03f941b
Show file tree
Hide file tree
Showing 10 changed files with 286 additions and 99 deletions.
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
14 changes: 7 additions & 7 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ model User {
blockedByAdmin DateTime?
onBoard Boolean @default(false)
bookmark Bookmark[]
companyId String? @unique
company Company? @relation(fields: [companyId], references: [id])
}
Expand Down Expand Up @@ -83,18 +82,19 @@ 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 {
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

0 comments on commit 03f941b

Please sign in to comment.