diff --git a/apps/app/src/app/(app)/[orgId]/settings/trust-portal/components/logos.tsx b/apps/app/src/app/(app)/[orgId]/settings/trust-portal/components/logos.tsx
index d353f1282..433e97f1d 100644
--- a/apps/app/src/app/(app)/[orgId]/settings/trust-portal/components/logos.tsx
+++ b/apps/app/src/app/(app)/[orgId]/settings/trust-portal/components/logos.tsx
@@ -1,61 +1,5 @@
import * as React from 'react';
-export const SOC2 = (props: React.SVGProps
) => (
-
-);
-
export const ISO27001 = (props: React.SVGProps) => (
);
+
+export const ISO42001 = (props: React.SVGProps) => (
+
+);
diff --git a/apps/app/src/app/(app)/[orgId]/settings/trust-portal/page.tsx b/apps/app/src/app/(app)/[orgId]/settings/trust-portal/page.tsx
index 919ebecc7..59cbf66a8 100644
--- a/apps/app/src/app/(app)/[orgId]/settings/trust-portal/page.tsx
+++ b/apps/app/src/app/(app)/[orgId]/settings/trust-portal/page.tsx
@@ -26,12 +26,14 @@ export default async function TrustPortalSettings({
soc2type1={trustPortal?.soc2type1 ?? false}
soc2type2={trustPortal?.soc2type2 ?? false}
iso27001={trustPortal?.iso27001 ?? false}
+ iso42001={trustPortal?.iso42001 ?? false}
gdpr={trustPortal?.gdpr ?? false}
hipaa={trustPortal?.hipaa ?? false}
pcidss={trustPortal?.pcidss ?? false}
soc2type1Status={trustPortal?.soc2type1Status ?? 'started'}
soc2type2Status={trustPortal?.soc2type2Status ?? 'started'}
iso27001Status={trustPortal?.iso27001Status ?? 'started'}
+ iso42001Status={trustPortal?.iso42001Status ?? 'started'}
gdprStatus={trustPortal?.gdprStatus ?? 'started'}
hipaaStatus={trustPortal?.hipaaStatus ?? 'started'}
pcidssStatus={trustPortal?.pcidssStatus ?? 'started'}
@@ -71,12 +73,14 @@ const getTrustPortal = cache(async (orgId: string) => {
soc2type1: trustPortal?.soc2type1,
soc2type2: trustPortal?.soc2type2 || trustPortal?.soc2,
iso27001: trustPortal?.iso27001,
+ iso42001: trustPortal?.iso42001,
gdpr: trustPortal?.gdpr,
hipaa: trustPortal?.hipaa,
pcidss: trustPortal?.pci_dss,
soc2type1Status: trustPortal?.soc2type1_status,
soc2type2Status: !trustPortal?.soc2type2 && trustPortal?.soc2 ? trustPortal?.soc2_status : trustPortal?.soc2type2_status,
iso27001Status: trustPortal?.iso27001_status,
+ iso42001Status: trustPortal?.iso42001_status,
gdprStatus: trustPortal?.gdpr_status,
hipaaStatus: trustPortal?.hipaa_status,
pcidssStatus: trustPortal?.pci_dss_status,
diff --git a/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/components/TaskPropertiesSidebar.tsx b/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/components/TaskPropertiesSidebar.tsx
index 4bd940bf8..d20b9f3ad 100644
--- a/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/components/TaskPropertiesSidebar.tsx
+++ b/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/components/TaskPropertiesSidebar.tsx
@@ -14,8 +14,6 @@ import { useState } from 'react';
import { TaskStatusIndicator } from '../../components/TaskStatusIndicator';
import { PropertySelector } from './PropertySelector';
import { DEPARTMENT_COLORS, taskDepartments, taskFrequencies, taskStatuses } from './constants';
-import { Popover, PopoverContent, PopoverTrigger } from '@comp/ui/popover';
-import { Calendar } from '@comp/ui/calendar';
import { format } from 'date-fns';
interface TaskPropertiesSidebarProps {
@@ -40,15 +38,6 @@ export function TaskPropertiesSidebar({
orgId,
}: TaskPropertiesSidebarProps) {
const [dropdownOpen, setDropdownOpen] = useState(false);
- const [isDatePickerOpen, setIsDatePickerOpen] = useState(false);
- const [tempDate, setTempDate] = useState(undefined);
-
- // Function to handle date confirmation
- const handleDateConfirm = (date: Date | undefined) => {
- setTempDate(date);
- setIsDatePickerOpen(false);
- handleUpdateTask({ reviewDate: date });
- };
return (
diff --git a/apps/app/src/jobs/tasks/task/task-schedule.ts b/apps/app/src/jobs/tasks/task/task-schedule.ts
new file mode 100644
index 000000000..303d781ed
--- /dev/null
+++ b/apps/app/src/jobs/tasks/task/task-schedule.ts
@@ -0,0 +1,243 @@
+import { db } from '@db';
+import { sendTaskReviewNotificationEmail } from '@trycompai/email';
+import { logger, schedules } from '@trigger.dev/sdk';
+
+export const taskSchedule = schedules.task({
+ id: 'task-schedule',
+ cron: '0 */12 * * *', // Every 12 hours
+ maxDuration: 1000 * 60 * 10, // 10 minutes
+ run: async () => {
+ const now = new Date();
+
+ // Find all Done tasks that have a review date and frequency set
+ const candidateTasks = await db.task.findMany({
+ where: {
+ status: 'done',
+ reviewDate: {
+ not: null,
+ },
+ frequency: {
+ not: null,
+ },
+ },
+ include: {
+ organization: {
+ select: {
+ name: true,
+ },
+ },
+ assignee: {
+ include: {
+ user: true,
+ },
+ },
+ },
+ });
+
+ // Helpers to compute next due date based on frequency
+ const addDaysToDate = (date: Date, days: number) => {
+ const result = new Date(date.getTime());
+ result.setDate(result.getDate() + days);
+ return result;
+ };
+
+ const addMonthsToDate = (date: Date, months: number) => {
+ const result = new Date(date.getTime());
+ const originalDayOfMonth = result.getDate();
+ result.setMonth(result.getMonth() + months);
+ // Handle month rollover (e.g., Jan 31 + 1 month -> Feb 28/29)
+ if (result.getDate() < originalDayOfMonth) {
+ result.setDate(0);
+ }
+ return result;
+ };
+
+ const overdueTasks = candidateTasks.filter((task) => {
+ if (!task.reviewDate || !task.frequency) return false;
+
+ let nextDueDate: Date | null = null;
+ switch (task.frequency) {
+ case 'daily':
+ nextDueDate = addDaysToDate(task.reviewDate, 1);
+ break;
+ case 'weekly':
+ nextDueDate = addDaysToDate(task.reviewDate, 7);
+ break;
+ case 'monthly':
+ nextDueDate = addMonthsToDate(task.reviewDate, 1);
+ break;
+ case 'quarterly':
+ nextDueDate = addMonthsToDate(task.reviewDate, 3);
+ break;
+ case 'yearly':
+ nextDueDate = addMonthsToDate(task.reviewDate, 12);
+ break;
+ default:
+ nextDueDate = null;
+ }
+
+ return nextDueDate !== null && nextDueDate <= now;
+ });
+
+ logger.info(`Found ${overdueTasks.length} tasks past their computed review deadline`);
+
+ if (overdueTasks.length === 0) {
+ return {
+ success: true,
+ totalTasksChecked: 0,
+ updatedTasks: 0,
+ message: 'No tasks found past their computed review deadline',
+ };
+ }
+
+ // Update all overdue tasks to "todo" status
+ try {
+ const taskIds = overdueTasks.map((task) => task.id);
+
+ const updateResult = await db.task.updateMany({
+ where: {
+ id: {
+ in: taskIds,
+ },
+ },
+ data: {
+ status: 'todo',
+ },
+ });
+
+
+
+ // Log details about updated tasks
+ overdueTasks.forEach((task) => {
+ logger.info(
+ `Updated task "${task.title}" (${task.id}) from org "${task.organization.name}" - frequency ${task.frequency} - last reviewed ${task.reviewDate?.toISOString()}`,
+ );
+ });
+
+ logger.info(`Successfully updated ${updateResult.count} tasks to "todo" status`);
+
+ // Build a map of admins by organization for targeted notifications
+ const uniqueOrgIds = Array.from(new Set(overdueTasks.map((t) => t.organizationId)));
+ const admins = await db.member.findMany({
+ where: {
+ organizationId: { in: uniqueOrgIds },
+ isActive: true,
+ // role is a comma-separated string sometimes
+ role: { contains: 'admin' },
+ },
+ include: {
+ user: true,
+ },
+ });
+
+ const adminsByOrgId = new Map