migration of job_id tags in notes and updated mine filter
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { type Component, createMemo, For, Show } from "solid-js";
|
import { type Component, createMemo, createResource, For, Show } from "solid-js";
|
||||||
import { store, getFavoriteTag } from "@/store";
|
import { store, getFavoriteTag } from "@/store";
|
||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import { Plus, Lock, Star } from "lucide-solid";
|
import { Plus, Lock, Star } from "lucide-solid";
|
||||||
@@ -12,6 +12,35 @@ export const NotesSidebar: Component<{
|
|||||||
setSelectedNoteId: (id: string | null) => void;
|
setSelectedNoteId: (id: string | null) => void;
|
||||||
}> = (props) => {
|
}> = (props) => {
|
||||||
const currentUserId = pb.authStore.model?.id;
|
const currentUserId = pb.authStore.model?.id;
|
||||||
|
const [accessibleJobs] = createResource(currentUserId, async (userId) => {
|
||||||
|
if (!userId) return { ids: new Set<string>(), numbers: new Set<string>() };
|
||||||
|
try {
|
||||||
|
const items = await pb.collection('Job_Info_Prod').getFullList({
|
||||||
|
filter: [
|
||||||
|
`EstimatorUser = "${userId}"`,
|
||||||
|
`OfficeRepUser = "${userId}"`,
|
||||||
|
`ProjectManagerUser = "${userId}"`,
|
||||||
|
`Estimator = "${userId}"`,
|
||||||
|
`Office_Rep = "${userId}"`,
|
||||||
|
`Project_Manager = "${userId}"`
|
||||||
|
].join(" || "),
|
||||||
|
fields: "id,Job_Number",
|
||||||
|
requestKey: null
|
||||||
|
});
|
||||||
|
const ids = new Set<string>();
|
||||||
|
const numbers = new Set<string>();
|
||||||
|
items.forEach((r: any) => {
|
||||||
|
if (r.id) ids.add(String(r.id));
|
||||||
|
if (r.Job_Number !== undefined && r.Job_Number !== null && r.Job_Number !== "") {
|
||||||
|
numbers.add(String(r.Job_Number));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { ids, numbers };
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Failed to load job access list for notes filter:", e);
|
||||||
|
return { ids: new Set<string>(), numbers: new Set<string>() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const filteredNotes = createMemo(() => {
|
const filteredNotes = createMemo(() => {
|
||||||
const filter = store.noteFilter;
|
const filter = store.noteFilter;
|
||||||
@@ -56,7 +85,20 @@ export const NotesSidebar: Component<{
|
|||||||
|
|
||||||
// Owned By Me Filter
|
// Owned By Me Filter
|
||||||
if (filter.ownedByMe) {
|
if (filter.ownedByMe) {
|
||||||
n = n.filter(note => note.user === currentUserId);
|
const accessible = accessibleJobs();
|
||||||
|
n = n.filter(note => {
|
||||||
|
if (note.tags?.includes("prism_job")) {
|
||||||
|
if (!accessible) return false;
|
||||||
|
const jobIdTag = note.tags.find(t => t.startsWith("job_id:"));
|
||||||
|
const jobRef = jobIdTag?.split(":")[1]?.trim();
|
||||||
|
if (!jobRef) return false;
|
||||||
|
const isLegacyNumber = /^\d{4}$/.test(jobRef);
|
||||||
|
return isLegacyNumber
|
||||||
|
? accessible.numbers.has(jobRef)
|
||||||
|
: accessible.ids.has(jobRef);
|
||||||
|
}
|
||||||
|
return note.user === currentUserId;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starred Filter
|
// Starred Filter
|
||||||
|
|||||||
+101
-1
@@ -1095,6 +1095,105 @@ const runLegacyTagMigration = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const runLegacyJobIdTagMigration = async () => {
|
||||||
|
const currentUserId = pb.authStore.model?.id;
|
||||||
|
if (!currentUserId) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const userRec = await pb.collection('users').getOne(currentUserId, { requestKey: null });
|
||||||
|
const prefs = userRec.Taskgrid_pref || {};
|
||||||
|
|
||||||
|
if (prefs.migratedLegacyJobIdTagsV1) return;
|
||||||
|
|
||||||
|
// Fetch all Notes (all users)
|
||||||
|
const notes = await pb.collection(NOTES_COLLECTION).getFullList({
|
||||||
|
requestKey: null
|
||||||
|
});
|
||||||
|
|
||||||
|
const extractLegacyJobNumbers = (tags: string[] | undefined) => {
|
||||||
|
if (!tags || tags.length === 0) return [];
|
||||||
|
const nums: string[] = [];
|
||||||
|
tags.forEach(t => {
|
||||||
|
if (!t.startsWith("job_id:")) return;
|
||||||
|
const val = t.split(":")[1]?.trim();
|
||||||
|
if (val && /^\d{4}$/.test(val)) nums.push(val);
|
||||||
|
});
|
||||||
|
return nums;
|
||||||
|
};
|
||||||
|
|
||||||
|
const legacyNumbers = new Set<string>();
|
||||||
|
notes.forEach(n => extractLegacyJobNumbers(n.tags).forEach(nm => legacyNumbers.add(nm)));
|
||||||
|
|
||||||
|
if (legacyNumbers.size > 0) {
|
||||||
|
const allNumbers = Array.from(legacyNumbers);
|
||||||
|
const jobMap = new Map<string, string>(); // jobNumber -> jobId
|
||||||
|
const chunkSize = 50;
|
||||||
|
|
||||||
|
for (let i = 0; i < allNumbers.length; i += chunkSize) {
|
||||||
|
const chunk = allNumbers.slice(i, i + chunkSize);
|
||||||
|
const filter = chunk.map(n => `Job_Number = "${n}"`).join(" || ");
|
||||||
|
const jobs = await pb.collection('Job_Info_Prod').getFullList({
|
||||||
|
filter,
|
||||||
|
fields: "id,Job_Number",
|
||||||
|
requestKey: null
|
||||||
|
});
|
||||||
|
jobs.forEach((j: any) => {
|
||||||
|
if (j.Job_Number !== undefined && j.Job_Number !== null && j.Job_Number !== "") {
|
||||||
|
jobMap.set(String(j.Job_Number), String(j.id));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let migratedNotes = 0;
|
||||||
|
for (const n of notes) {
|
||||||
|
if (!n.tags || n.tags.length === 0) continue;
|
||||||
|
let changed = false;
|
||||||
|
const newTags = n.tags.map((tag: string) => {
|
||||||
|
if (!tag.startsWith("job_id:")) return tag;
|
||||||
|
const val = tag.split(":")[1]?.trim();
|
||||||
|
if (!val || !/^\d{4}$/.test(val)) return tag;
|
||||||
|
const mappedId = jobMap.get(val);
|
||||||
|
if (!mappedId) return tag;
|
||||||
|
changed = true;
|
||||||
|
return `job_id:${mappedId}`;
|
||||||
|
});
|
||||||
|
if (changed) {
|
||||||
|
await pb.collection(NOTES_COLLECTION).update(n.id, { tags: newTags }, { requestKey: null });
|
||||||
|
migratedNotes++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (migratedNotes > 0) {
|
||||||
|
setStore("notes", (items) => items.map(n => {
|
||||||
|
if (!n.tags || n.tags.length === 0) return n;
|
||||||
|
let localChanged = false;
|
||||||
|
const nt = n.tags.map(tag => {
|
||||||
|
if (!tag.startsWith("job_id:")) return tag;
|
||||||
|
const val = tag.split(":")[1]?.trim();
|
||||||
|
if (!val || !/^\d{4}$/.test(val)) return tag;
|
||||||
|
const mappedId = jobMap.get(val);
|
||||||
|
if (!mappedId) return tag;
|
||||||
|
localChanged = true;
|
||||||
|
return `job_id:${mappedId}`;
|
||||||
|
});
|
||||||
|
return localChanged ? { ...n, tags: nt } : n;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[MIGRATION] Job tag migration complete. Updated ${migratedNotes} notes.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
prefs.migratedLegacyJobIdTagsV1 = true;
|
||||||
|
await pb.collection('users').update(currentUserId, { Taskgrid_pref: prefs }, { requestKey: null });
|
||||||
|
|
||||||
|
if (pb.authStore.model) {
|
||||||
|
pb.authStore.model.Taskgrid_pref = prefs;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[MIGRATION] Job tag migration failed:", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const initStore = async () => {
|
export const initStore = async () => {
|
||||||
if (!pb.authStore.isValid || store.isInitializing) return;
|
if (!pb.authStore.isValid || store.isInitializing) return;
|
||||||
setStore("isInitializing", true);
|
setStore("isInitializing", true);
|
||||||
@@ -1438,8 +1537,9 @@ export const initStore = async () => {
|
|||||||
// Fire off background sync
|
// Fire off background sync
|
||||||
await backgroundSync();
|
await backgroundSync();
|
||||||
|
|
||||||
// Run one-time migration for legacy tags
|
// Run one-time migrations
|
||||||
await runLegacyTagMigration();
|
await runLegacyTagMigration();
|
||||||
|
await runLegacyJobIdTagMigration();
|
||||||
|
|
||||||
// Separate Templates load (less critical)
|
// Separate Templates load (less critical)
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user