Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin' into vin/1790/allow-deleting-co…
Browse files Browse the repository at this point in the history
…ntacts
  • Loading branch information
vinaybadgujar102 committed Dec 15, 2024
2 parents a0e8f09 + 2380f37 commit c67f3a1
Show file tree
Hide file tree
Showing 45 changed files with 698 additions and 147 deletions.
4 changes: 4 additions & 0 deletions src/Interfaces/ExportStrategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
export interface ExportStrategy {
export(options?: Record<string, any>): Promise<void>;
}
2 changes: 2 additions & 0 deletions src/Interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ export interface ILocationState {
export interface ImpossibleGoal extends GoalItem {
impossible: boolean;
}

export type ScheduleStatus = "pending" | "scheduled" | "impossible" | "future" | null;
6 changes: 6 additions & 0 deletions src/api/GoalsAPI/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { getInstallId } from "@src/utils";
import { IHintRequestBody } from "@src/models/HintItem";
import { sortGoalsByProps } from "../GCustomAPI";
import { deleteAvailableGoalHint, deleteHintItem, getGoalHintItem } from "../HintsAPI";
import { deleteTasksDoneTodayByGoalId } from "../TasksDoneTodayAPI";
import { deleteTaskHistoryItem } from "../TaskHistoryAPI";

export const addDeletedGoal = async (goal: GoalItem) => {
await db
Expand Down Expand Up @@ -173,6 +175,8 @@ export const removeChildrenGoals = async (parentGoalId: string) => {
childrenGoals.forEach((goal) => {
removeChildrenGoals(goal.id);
removeGoal(goal);
deleteTasksDoneTodayByGoalId(goal.id);
deleteTaskHistoryItem(goal.id);
});
};

Expand Down Expand Up @@ -315,6 +319,8 @@ export const notifyNewColabRequest = async (id: string, relId: string) => {
export const removeGoalWithChildrens = async (goal: GoalItem) => {
await removeChildrenGoals(goal.id);
await removeGoal(goal);
await deleteTasksDoneTodayByGoalId(goal.id);
await deleteTaskHistoryItem(goal.id);
if (goal.parentGoalId !== "root") {
getGoal(goal.parentGoalId).then(async (parentGoal: GoalItem) => {
const parentGoalSublist = parentGoal.sublist;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
/* eslint-disable no-param-reassign */
import { db } from "@models";
import { DumpboxItem } from "@src/models/DumpboxItem";
import { SchedulerOutputCacheItem } from "@src/models/SchedulerOutputCacheItem";
import { v4 as uuidv4 } from "uuid";

export const getFromOutbox = async (key: string) => {
export const getSchedulerCachedRes = async (key: string) => {
try {
const dumpbox = await db.dumpboxCollection.where("key").equals(key).toArray();
return dumpbox[0];
const schedulerOutputCache = await db.schedulerOutputCacheCollection.where("key").equals(key).toArray();
return schedulerOutputCache[0];
} catch (err) {
return null;
}
};

export const addSchedulerRes = async (uniqueId: string, output: string) => {
export const addSchedulerResToCache = async (uniqueId: string, output: string) => {
let newId;
await db
.transaction("rw", db.dumpboxCollection, async () => {
newId = await db.dumpboxCollection.add({
.transaction("rw", db.schedulerOutputCacheCollection, async () => {
newId = await db.schedulerOutputCacheCollection.add({
key: "scheduler",
value: JSON.stringify({
uniqueId,
Expand All @@ -32,11 +32,11 @@ export const addSchedulerRes = async (uniqueId: string, output: string) => {
};

export const updateSchedulerCachedRes = async (uniqueId: string, output: string) => {
db.transaction("rw", db.dumpboxCollection, async () => {
await db.dumpboxCollection
db.transaction("rw", db.schedulerOutputCacheCollection, async () => {
await db.schedulerOutputCacheCollection
.where("key")
.equals("scheduler")
.modify((obj: DumpboxItem) => {
.modify((obj: SchedulerOutputCacheItem) => {
obj.value = JSON.stringify({
uniqueId,
output,
Expand Down
22 changes: 22 additions & 0 deletions src/api/TaskHistoryAPI/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { db } from "@src/models";
import { TaskHistoryEvents, TaskHistoryItem } from "@src/models/TaskHistoryItem";
import { ITask } from "@src/Interfaces/Task";

export async function addTaskActionEvent(task: ITask, eventType: TaskHistoryEvents) {
if (!task) return;

const newEvent: TaskHistoryItem = {
goalId: task.goalid,
eventType,
duration: task.duration,
scheduledStart: task.start,
scheduledEnd: task.deadline,
eventTime: new Date().toISOString(),
};

await db.taskHistoryCollection.add(newEvent);
}

export async function deleteTaskHistoryItem(goalId: string) {
await db.taskHistoryCollection.where("goalId").equals(goalId).delete();
}
40 changes: 0 additions & 40 deletions src/api/TasksAPI/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { blockedSlotOfTask, TaskItem } from "@src/models/TaskItem";
import { GoalItem } from "@src/models/GoalItem";
import { calDays, convertDateToString, getLastDayDate } from "@src/utils";
import { convertDateToDay } from "@src/utils/SchedulerUtils";
import { ITask } from "@src/Interfaces/Task";
import { ISchedulerInputGoal } from "@src/Interfaces/IScheduler";
import { getGoal } from "../GoalsAPI";

Expand Down Expand Up @@ -106,45 +105,6 @@ export const refreshTaskCollection = async () => {
console.error("Error updating field:", error);
}
};
export const completeTask = async (id: string, duration: number, task: ITask) => {
db.transaction("rw", db.taskCollection, async () => {
await db.taskCollection
.where("id")
.equals(id)
.modify((obj: TaskItem) => {
obj.completedToday += duration;
obj.completedTodayTimings.push({
goalid: task.goalid,
start: task.start,
deadline: task.deadline,
});
obj.completedTodayIds.push(task.taskid);
});
}).catch((e) => {
console.log(e.stack || e);
});
};

export const skipTask = async (id: string, period: string, task: ITask) => {
db.transaction("rw", db.taskCollection, async () => {
await db.taskCollection
.where("id")
.equals(id)
.modify((obj: TaskItem) => {
obj.skippedToday.push(period);
obj.completedTodayTimings.push({
goalid: task.goalid,
start: task.start,
deadline: task.deadline,
});
if (obj.skippedToday.length > 1) {
obj.skippedToday.sort((a, b) => Number(a.split("-")[0]) - Number(b.split("-")[0]));
}
});
}).catch((e) => {
console.log(e.stack || e);
});
};

export const getAllTasks = async () => {
const allGoals = await db.taskCollection.toArray();
Expand Down
44 changes: 44 additions & 0 deletions src/api/TasksDoneTodayAPI/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { db } from "@src/models";
import { TasksDoneTodayItem } from "@src/models/TasksDoneTodayItem";

export const addTaskDoneToday = async (completedTask: TasksDoneTodayItem) => {
try {
await db.tasksDoneTodayCollection.add(completedTask);
} catch (error) {
console.error("Error adding task:", error);
}
};

export const getAllTasksDoneToday = async () => {
try {
const tasks = await db.tasksDoneTodayCollection.toArray();
return tasks;
} catch (error) {
console.error("Error fetching tasks:", error);
return [];
}
};

export const deleteTaskDoneToday = async (id: string) => {
try {
await db.tasksDoneTodayCollection.delete(id);
} catch (error) {
console.error("Error deleting task:", error);
}
};

export const deleteTasksDoneTodayByGoalId = async (goalId: string) => {
try {
await db.tasksDoneTodayCollection.where("goalId").equals(goalId).delete();
} catch (error) {
console.error("Error deleting tasks by goalId:", error);
}
};

export const deleteAllTasksDoneToday = async () => {
try {
await db.tasksDoneTodayCollection.clear();
} catch (error) {
console.error("Error clearing tasks:", error);
}
};
31 changes: 14 additions & 17 deletions src/components/BackupRestoreModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,23 @@ import { backupRestoreModal, darkModeState, displayToast, lastAction } from "@sr

import "dexie-export-import";
import "./index.scss";
import { JsonExportStrategy } from "@src/utils/ExportStrategies/JsonExportStrategy";
import { CsvExportStrategy } from "@src/utils/ExportStrategies/CsvExportStrategy";
import { ExportManager } from "../utils/ExportStrategies/ExportManager";

const backupImg =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAADB0lEQVR4nO2Z3YtMYRzHP3a9jpc21sxQ5C1FFFHakJeibd24EQnXe4H2brkTpVUU+QMWiVLWhfdEsSHvNizLtkoJid3FLlvs6Nm+R481M7szc87OeTSfOs0855zn9/y+53n7nd+BAgUKhIHBwAJgE7Ad2AqsBRYCxTjAIuAI8BVIpDg+ArVAGSFkAnAK6LYcfg6cAA4Bh4E64FkvUXXAdELCcuC9HPsM7AYmpbl/BrALaFedVqCcPLMa+C6HTgLRDOqOV4+Zuj+BLeSJ2cA3ObIzSxuDVNcMyS5gCQER0UqzUkPCYwjwVCL2+NDOXtkyQ7QEH5kGHAc6e03ORmAdUKXydaDIh/aKgKuyWYNPlFsT0YzdR8A14LUlyAyFX8AsvxoF5smmeXgxP4x1yNlaLas2ZRLmLZ1+c0a2K3M1dFOG9vUxbyr9eGpJ2Kz2L5ID82XklSZzPojKh0+5GKnycRXKhU75MSKbykuB+zKQt41JtMiPqWRAzJpgXkBn7xf54K18OQCsAYb2VWGmtaS2aKJl1Z0+86TX3vUBqE41b8cBzbrxNDCK8DAGWAxs1KbcJT/vJovljuni5TyuUP3FRBm3rcjCCP0T7HVr956IGwxXOJRQL/WwXycO4hZx4Is6wUQgPJCQZbhHjd0JrSqMDagxMwTuBZgTSGhl44cKwwJqzFs2g6BEtttM4Y0K6d6rwypkpGybN1OuqLDeQSFzZPulKWxT4ayDQqqtdyVKrTdAk8pxRUjEisMqvJM7dOIdMMURIUdl95ayLz2Y/OsFS8yKEAuJWCLaFZn8xWjgvNXwOWADMLk/oXPAQiLAXM0JbziZXX1VqgrFurktTdI51VGfhZD6LNpJaDj90xPJKFXq/5L2GS90TnfcCFBIB9Ck1anCnhMDSZDL74BSEBI2nOyRh8BjvfSkEmKuNQB3CDEN1me2eBIhcV1LKGccWqLWd5Im5QA8ITHr2gsX8gNx66l7v/Z/u7dCT9R6+vbhRE/0JcZJER5xJdEaXRpO6Xomk8/UBfgf+A3SSyxFIXLo1QAAAABJRU5ErkJggg==";
const restoreImg =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAADnUlEQVR4nO2ay05UQRCGPyMMkyg3AVHcyE6jQZ5CQUVBV6hbI1FE5AHEpUFXRl4EMYTERBFBkIuSqAjISmWhxAUaLkIc08l/kg7OMJc+5zBj+JMOM3Sfrq5T1d1Vfw3s4P9FEXARuAf0AjPAN2AVWAN+ANPAE+A+cB4oDXpRo8CrFMblA83AU+A3EEuzbQD9wBWgIAhFPEGJEAE6gAVr7DrwDOiUZY4AFVpgvt7+UaARuKOxq9bzXzVnNCxFzgBz1pgp4GqGblKiZ8et+T4B9QSoiHlTj4A/6nvnp0CgDnhjye72wzqbFdkPjOl/xh1uA3n4j93ALWBZsozMSr8UOQzM6rtxqVqCR41OPU9mtasi5daE47JMWCgFXkr2PHDARZER/X0NFBI+9lhrGMtkz9hn/byrnzqizPIKcwCkhVQvtEHCQY11AJjTzXdFXhAe2q3N7+ulGTbygLdSxiiV0zgtRRZy3Sq7gAkpc4kcxzUp0keOo1T5jYm2i4MWZkL3m7rMfqmZ/KZVKYArnssq5wgQh6zTJV6bBKocZdzVXF0EaAlPifdKZwvVGpXqxrRhXSzTpHkeExDaJOBDAv8ttpS54SDnmOb4SIC5fkyWSPY2U+EEEqFcc3yP1znoQ+z0UwIKk7AtZsySowvHlOT9gzV1uvjuUgqKFAetyKI6TdicKbzcwWzsRLigMcNBudasOg1lkylaNcd0gs1eYuUW14Pa7L0pbNRkiOie8IQ0aU8UyRJ22my4rkzRpHl64nU+UKchz1xQZSkTrxklDgZ5ITaq0zCArojonhjWSWbakNzJxRIeBrTWBhIEYxs6CYwvZyv2WUGjcdm46JemhsbMVrRojYbVT4jLlh9na2I1qTWaKsCWF80XDTxF9uGs1vY5lTJEhxVuGy42m8iHKa3NBKdJERW1HxOhnC3o0Jpm0ikK1euhZZFj241aYEVrOpnuw93WG3CJv1xRYRWWHmYyQdSqiYyIUA4be60cZ9SlzlihmMlj5M33MC++Iasc50ykV1umNW52gnD2xJxkzqrY5AsqLTdbFvcaROktT6fTiuVOvheXotYBEBNbUu/jjW2qxd494W3sQGrvHuo2lacnRGOWZrgPWjaF/bOZHLEu1mm3wpmYItIB/WCgSVlcmUL6iD4fV4LVqbEeT+CFHW1BWyERCsSK9ykFiKXZ1hXFNm+XAvFQrESnSwzgtAiNNbVFEXc9GtOwVT6xA3IcfwEzCVxsbkJcggAAAABJRU5ErkJggg==";

const exportManager = new ExportManager(new JsonExportStrategy());

const BackupRestoreModal = () => {
const open = useRecoilValue(backupRestoreModal);
const darkModeStatus = useRecoilValue(darkModeState);
const setShowToast = useSetRecoilState(displayToast);
const setLastAction = useSetRecoilState(lastAction);

const backupData = async () => {
const file = await db.export({ prettyJson: true });
const blob = new Blob([file], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `ZinZenBackup-${new Date().toLocaleDateString()}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};

const importSuccessfull = () => {
setLastAction("goalsRestored");
window.history.back();
Expand Down Expand Up @@ -80,32 +73,36 @@ const BackupRestoreModal = () => {
}
};

const getOption = (text: "Backup" | "Restore") => (
const getOption = (text: "Backup" | "CSV Export" | "Restore") => (
<button
type="button"
className={`default-btn${darkModeStatus ? "-dark" : ""}`}
style={{ display: "flex", flexDirection: "column", gap: 4 }}
onClick={async () => {
if (text === "Backup") {
await backupData();
window.history.back();
exportManager.setStrategy(new JsonExportStrategy());
await exportManager.export();
} else if (text === "CSV Export") {
exportManager.setStrategy(new CsvExportStrategy());
await exportManager.export();
} else {
document.getElementById("backupFileInput")?.click();
}
}}
>
<img className="secondary-icon" alt={`${text} data`} src={text === "Backup" ? backupImg : restoreImg} />
<img className="secondary-icon" alt={`${text}`} src={text === "Restore" ? restoreImg : backupImg} />
<p>{text}</p>
</button>
);

return (
<ZModal open={open} onCancel={() => window.history.back()} type="backupRestoreModal">
<p className="popupModal-title" style={{ textAlign: "center" }}>
{" "}
Choose an option from below
</p>
<div style={{ display: "flex", justifyContent: "center", gap: "60px" }}>
{getOption("Backup")}
{getOption("CSV Export")}
{getOption("Restore")}
</div>
<input type="file" id="backupFileInput" style={{ display: "none" }} onChange={restoreData} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ITagsChanges } from "@src/Interfaces/IDisplayChangesModal";
import { sendNewGoals } from "@src/helpers/BatchPublisher";
import { darkModeState, lastAction } from "@src/store";
import { getAllContacts } from "@src/api/ContactsAPI";
import { sendUpdatedGoal } from "@src/helpers/PubSubController";
import { sendUpdatedGoal } from "@src/controllers/PubSubController";
import { typeOfChange, typeOfIntent } from "@src/models/InboxItem";
import { archiveUserGoal, getGoal, removeGoalWithChildrens, updateGoal } from "@src/api/GoalsAPI";
import { deleteGoalChangesInID, getInboxItem, removeGoalInbox, removePPTFromInboxOfGoal } from "@src/api/InboxAPI";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,8 @@
gap: 10px;
justify-content: center;
}

.schedule-status {
margin-top: 8px;
text-align: center;
}
60 changes: 59 additions & 1 deletion src/components/GoalsComponents/GoalConfigModal/ConfigGoal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ import { useParentGoalContext } from "@src/contexts/parentGoal-context";
import useGoalActions from "@src/hooks/useGoalActions";
import useGoalStore from "@src/hooks/useGoalStore";
import { unarchiveUserGoal } from "@src/api/GoalsAPI";
import { ILocationState } from "@src/Interfaces";
import { ILocationState, ScheduleStatus } from "@src/Interfaces";
import { useLocation, useNavigate } from "react-router-dom";
import { suggestedGoalState } from "@src/store/SuggestedGoalState";
import { getHistoryUptoGoal } from "@src/helpers/GoalProcessor";
import { ISchedulerOutput } from "@src/Interfaces/IScheduler";
import useScheduler from "@src/hooks/useScheduler";
import { colorPalleteList, calDays, convertOnFilterToArray, getSelectedLanguage } from "../../../utils";

import "./ConfigGoal.scss";
Expand Down Expand Up @@ -48,6 +50,9 @@ const ConfigGoal = ({ type, goal, mode }: { type: TGoalCategory; mode: TGoalConf
const location = useLocation();
const navigate = useNavigate();

const { checkGoalSchedule } = useScheduler();
const [scheduleStatus, setScheduleStatus] = useState<ScheduleStatus>(null);

let defaultColorIndex = Math.floor(Math.random() * colorPalleteList.length - 1) + 1;
let defaultAfterTime = isEditMode ? (goal.afterTime ?? 9) : 9;
let defaultBeforeTime = isEditMode ? (goal.beforeTime ?? 18) : 18;
Expand Down Expand Up @@ -281,6 +286,53 @@ const ConfigGoal = ({ type, goal, mode }: { type: TGoalCategory; mode: TGoalConf

const titlePlaceholder = t(`${type !== "Budget" ? "goal" : "budget"}Title`);

const checkSchedulingStatus = async (schedulerOutput: ISchedulerOutput | undefined, goalId: string) => {
if (!schedulerOutput) return "pending";

const { scheduled, impossible } = schedulerOutput;

if (impossible?.some((task) => task.id === goalId)) {
return "impossible";
}

const isScheduledInNext7Days = scheduled.some((day) => day.tasks.some((task) => task.goalid === goalId));

return isScheduledInNext7Days ? "scheduled" : "future";
};

const getScheduleStatusText = (status: ScheduleStatus) => {
switch (status) {
case "scheduled":
return "Auto scheduled";
case "impossible":
return "! Impossible";
case "future":
return "Scheduled someday";
default:
return "";
}
};

const checkSchedule = async () => {
setScheduleStatus("pending");
try {
const schedulerOutput = await checkGoalSchedule(getFinalTags());
const status = await checkSchedulingStatus(schedulerOutput, goal.id);
setScheduleStatus(status);
} catch (error) {
setScheduleStatus(null);
}
};

useEffect(() => {
const debounceTimer = setTimeout(() => {
console.log("checking schedule");
checkSchedule();
}, 1000);

return () => clearTimeout(debounceTimer);
}, [tags.duration, afterTime, beforeTime, perDayHrs, perWeekHrs]);

return (
<ZModal
open
Expand Down Expand Up @@ -418,6 +470,9 @@ const ConfigGoal = ({ type, goal, mode }: { type: TGoalCategory; mode: TGoalConf
{t(`${action} Budget`)}
</button>
</div>
{scheduleStatus && (
<div className={`schedule-status ${scheduleStatus}`}>{getScheduleStatusText(scheduleStatus)}</div>
)}
</>
) : (
<div className="d-flex f-col gap-16">
Expand Down Expand Up @@ -448,6 +503,9 @@ const ConfigGoal = ({ type, goal, mode }: { type: TGoalCategory; mode: TGoalConf
disablePastDates
/>
</div>
{scheduleStatus && tags.duration && (
<div className={`schedule-status ${scheduleStatus}`}>{getScheduleStatusText(scheduleStatus)}</div>
)}
</div>
)}
</div>
Expand Down
Loading

0 comments on commit c67f3a1

Please sign in to comment.