Skip to content

dashboard#108

Merged
ChitkulLakshya merged 5 commits into
mainfrom
dashboard
Apr 20, 2026
Merged

dashboard#108
ChitkulLakshya merged 5 commits into
mainfrom
dashboard

Conversation

@ChitkulLakshya
Copy link
Copy Markdown
Collaborator

  • fixed the email
  • feat: finalize boneyard capture
  • feat: implement team logo system, refactor settings UI, and cleanup file structure
  • fixed the dependencey and brought backe the welcome page

Description

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that changes existing functionality)
  • Documentation update
  • Refactor (no functional changes)
  • CI/CD changes

Related Issues

Screenshots (if applicable)

Testing

  • Tested on Windows
  • Tested on macOS
  • Tested on Linux
  • Unit tests added/updated
  • Manual testing completed

Checklist

  • Code follows project coding standards
  • Self-review of code performed
  • Comments added for complex logic
  • Documentation updated (if applicable)
  • All tests pass locally
  • No new warnings or errors introduced

Copilot AI review requested due to automatic review settings April 20, 2026 11:11
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
zync Building Building Preview, Comment Apr 20, 2026 11:11am

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the dashboard experience by restoring/improving the welcome flow, adding team logo + team management enhancements, and introducing Firestore-backed persistence for team/task analytics, alongside backend notification/security tweaks and dependency/config updates.

Changes:

  • Add team logo system and refactor team settings UI (including ownership transfer).
  • Add Firestore persistence hooks for team membership and task analytics, and extend Activity Log analytics/filtering UI.
  • Backend: tighten /api/users/sync security/notifications and add /api/support route; bump tooling/deps and Boneyard registry artifacts.

Reviewed changes

Copilot reviewed 36 out of 40 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
vite.config.ts Removes optimizeDeps entry config; sets CSS minifier.
tsconfig.json Suppresses TypeScript deprecation warnings via ignoreDeprecations.
tsconfig.app.json Same ignoreDeprecations setting for app TS config.
src/pages/WelcomeToZync.tsx Guards /welcome by redirecting existing users post-auth.
src/pages/ProjectDetails.tsx Updates ActivityGraph import path (currently appears incorrect).
src/lib/team-logos.ts Adds team logo registry + helpers for deterministic/random selection.
src/lib/postLoginRedirect.ts Refines “fresh account” detection + fallback behavior for welcome routing.
src/lib/firebase.ts Exposes Firestore instance (db).
src/hooks/useTeamPersistence.ts New Firestore team membership sync hook.
src/hooks/useTaskPersistence.ts New Firestore task analytics persistence hook.
src/hooks/useSyncData.ts Removes verbose sync console logging.
src/hooks/useNotePresence.ts Gates debug logging behind import.meta.env.DEV.
src/hooks/use-user-sync.ts Adds sync-in-progress guard around user sync flow (but drops error catch).
src/hooks/use-activity-tracker.ts Prevents duplicate session starts by checking sessionIdRef before starting.
src/components/views/TasksView.tsx Adjusts SelectItem keys with fallback indices.
src/components/views/TaskDetailDrawer.tsx Tracks “task opened” via Firestore persistence hook.
src/components/views/SettingsView.tsx Refactors Team tab UI into selectable grid + adds ownership transfer UI/actions.
src/components/views/MobileView.tsx Switches several imports to relative paths (local refactor).
src/components/views/JoinTeamDialog.tsx Syncs join-team action to Firestore for analytics.
src/components/views/DesktopView.tsx Refactors Activity Log fetching (parallel + slower polling) and passes team data to ActivityLogView.
src/components/views/CreateTeamDialog.tsx Adds team logo picker + Firestore sync on team creation.
src/components/views/ActivityLogView.tsx Major redesign: team/member filters, merged team sources, persisted task stats, enhanced feed rendering.
src/components/ui/skeletons/index.ts Removes legacy skeleton re-exports (left intentionally empty).
src/bones/workspace-project-card.bones.json Adds Boneyard capture artifact for skeletons.
src/bones/task-list-item.bones.json Adds Boneyard capture artifact for skeletons.
src/bones/registry.ts Adds generated Boneyard registry and registers bones JSON.
src/bones/project-card-grid.bones.json Adds Boneyard capture artifact for skeletons.
src/bones/calendar-events-grid.bones.json Adds Boneyard capture artifact for skeletons.
package.json Forces Vite dev startup + bumps Vite version.
package-lock.json Lockfile updates for Vite and transitive deps.
backend/tests/user_sync_security.test.js Extends tests for new user notification + UID-mismatch behavior.
backend/services/googleMeet.js Trims GOOGLE_REFRESH_TOKEN before setting credentials.
backend/routes/userRoutes.js Adds UID mismatch protection and new-user notification dispatch logic using env recipients.
backend/routes/teamRoutes.js Adds PATCH /:teamId/transfer-ownership endpoint.
backend/routes/supportRoutes.js Switches support email sender to mailer service + updates default recipient.
backend/package.json Adds Jest dev dependency.
backend/index.js Registers /api/support route.
backend/.env.example Deleted.
.gitignore Adds docs/ to ignore list (and retains backend/).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

import { useTaskUpdates } from "@/hooks/use-task-updates";
import KanbanBoard from "@/components/workspace/KanbanBoard";
import { ActivityGraph } from "@/components/views/ActivityGraph";
import { ActivityGraph } from "@/components/views/activity/ActivityGraph";
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import path for ActivityGraph points to "@/components/views/activity/ActivityGraph", but the component file currently lives at "src/components/views/ActivityGraph.tsx". This will cause a module resolution/build failure. Update the import to the actual path (or add/move the file so the import matches the new structure).

Copilot uses AI. Check for mistakes.
Comment on lines +48 to +50
markTaskOpened(task.id);
}
}, [open, task?.id]);
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This effect calls markTaskOpened but it is missing from the dependency array, which will trip react-hooks/exhaustive-deps and can capture a stale function reference. Include markTaskOpened in deps (or memoize it in the hook) and consider calling it with void to make the fire-and-forget intent explicit.

Suggested change
markTaskOpened(task.id);
}
}, [open, task?.id]);
void markTaskOpened(task.id);
}
}, [markTaskOpened, open, task?.id]);

Copilot uses AI. Check for mistakes.
Comment on lines 311 to +330
const total = taskList.length;
const inProgress = taskList.filter(isInProgressTask).length;
const completed = taskList.filter(isCompletedTask).length;
const overdue = taskList.filter(isOverdueTask).length;
return { total, inProgress, completed, overdue };
}, [taskList]);
const completedCount = taskList.filter(isCompletedTask).length;

const hasAnyCommit = taskList.some(t => Boolean(
(t as any).commitUrl ||
(t as any).commitMessage ||
(t as any).commitInfo?.message
));
const inProgress = hasAnyCommit ? 1 : 0;
const efficiency = total ? Math.round((completedCount / total) * 100) : 0;

return {
total,
inProgress,
completed: completedCount,
overdue: persistedStats?.overdue || 0,
efficiency,
dailyActiveAvg: dailyStats.avgMins
};
}, [taskList, persistedStats, selectedUserId, currentUserId, dailyStats.avgMins]);
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

taskStats sets inProgress to 1 if any task has commit evidence, which makes the count incorrect (and ignores the existing isInProgressTask helper). Compute inProgress as the number of tasks matching the in-progress criteria so analytics are accurate.

Copilot uses AI. Check for mistakes.
<div className="flex items-center gap-3">
<div className="h-12 w-12 flex items-center justify-center rounded-xl bg-blue-500/10 border border-blue-500/20">
{(() => {
const lid = selectedTeam.logoId || getDeterministicLogoId(selectedTeam.id);
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

selectedTeam.id is used here, but elsewhere in this component teams may come back as { _id, ... } (you already normalize with id || _id earlier). If selectedTeam.id is undefined, deterministic logo selection will be inconsistent and can throw if downstream expects a string. Use selectedTeam.id || selectedTeam._id consistently.

Suggested change
const lid = selectedTeam.logoId || getDeterministicLogoId(selectedTeam.id);
const teamId = selectedTeam.id || selectedTeam._id;
const lid = selectedTeam.logoId || getDeterministicLogoId(teamId);

Copilot uses AI. Check for mistakes.
Comment on lines 1073 to 1079
setTeamsData((prev: any[]) => prev.filter(t => t.id !== teamId));
if (selectedTeamId === teamId) {
setSelectedTeamId(null);
}
setUserData((prev: any) => ({
...prev,
teamMemberships: prev.teamMemberships?.filter((id: string) => id !== teamId) || []
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Local state updates after leaving/deleting a team filter by t.id, but teams can also be keyed by _id in this component. This can leave deleted/left teams in UI state. Filter by (t.id || t._id) (and likewise compare teamId against both forms).

Suggested change
setTeamsData((prev: any[]) => prev.filter(t => t.id !== teamId));
if (selectedTeamId === teamId) {
setSelectedTeamId(null);
}
setUserData((prev: any) => ({
...prev,
teamMemberships: prev.teamMemberships?.filter((id: string) => id !== teamId) || []
setTeamsData((prev: any[]) => prev.filter(t => (t.id || t._id) !== teamId));
if (selectedTeamId === teamId || teamsData?.some((t: any) => (t.id === selectedTeamId || t._id === selectedTeamId) && (t.id === teamId || t._id === teamId))) {
setSelectedTeamId(null);
}
setUserData((prev: any) => ({
...prev,
teamMemberships: prev.teamMemberships?.filter((membership: any) => {
const membershipTeamId = typeof membership === "string" ? membership : (membership?.id || membership?._id);
return membershipTeamId !== teamId;
}) || []

Copilot uses AI. Check for mistakes.
Comment on lines +68 to +78
const joinTeamSync = async (inviteCode: string, userId: string) => {
try {
// Find team by invite code
const q = query(collection(db, "teams"), where("inviteCode", "==", inviteCode));
const unsubscribe = onSnapshot(q, (snapshot) => {
snapshot.forEach(async (teamDoc) => {
await updateDoc(doc(db, "teams", teamDoc.id), {
members: arrayUnion(userId)
});
});
});
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

joinTeamSync uses onSnapshot for what appears to be a one-time lookup, but never unsubscribes. This leaves a live listener running and can repeatedly re-apply updates. Use a one-time read (getDocs) or unsubscribe immediately after the first snapshot/update completes.

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +63
const markTaskOpened = async (taskId: string) => {
if (!userId) return;
// In Firestore, we should ideally track individual task statuses in a subcollection
// but to satisfy "Overdue = user just opened the task" simply, we can increment a counter or track in a map
try {
const docRef = doc(db, "tasks", userId);
const snap = await getDoc(docRef);
let currentStats = snap.exists() ? snap.data() as TaskStats : { total: 0, inProgress: 0, completed: 0, overdue: 0 };

// For now, let's just increment overdue if this is a "new" opening (simulated)
// A better way would be tracking specific task IDs in a sub-collection
await setDoc(docRef, { ...currentStats, overdue: (currentStats.overdue || 0) + 1 }, { merge: true });
} catch (error) {
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

markTaskOpened does a read-then-write (getDoc + setDoc) to increment overdue, which is non-atomic and can lose increments under concurrent calls. Prefer updateDoc with FieldValue.increment (or a transaction) and keep the default TaskStats shape consistent (include efficiency/dailyActiveAvg) to avoid partial documents.

Copilot uses AI. Check for mistakes.
Comment on lines +199 to +203
if (currentTeamId) {
setSelectedTeamId(currentTeamId);
} else if (selectedTeamId === 'all' && allTeams.length > 0) {
setSelectedTeamId(allTeams[0].id);
}
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This effect reads selectedTeamId but it isn't listed in the dependency array, which will fail react-hooks/exhaustive-deps. Add selectedTeamId to deps or restructure the logic so it doesn't rely on a closed-over state value.

Suggested change
if (currentTeamId) {
setSelectedTeamId(currentTeamId);
} else if (selectedTeamId === 'all' && allTeams.length > 0) {
setSelectedTeamId(allTeams[0].id);
}
setSelectedTeamId((prevSelectedTeamId) => {
if (currentTeamId) {
return currentTeamId;
}
if (prevSelectedTeamId === 'all' && allTeams.length > 0) {
return allTeams[0].id;
}
return prevSelectedTeamId;
});

Copilot uses AI. Check for mistakes.
Comment on lines +1089 to +1092
if (!currentUser) return;
const team = teamsData.find((t: any) => t.id === teamId);
const member = team?.memberDetails?.find((m: any) => m.uid === newOwnerId);

Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleTransferOwnership looks up the team with t.id === teamId, but the rest of the component treats IDs as t.id || t._id. If the API returns _id, member lookup (for the confirm text) will fail. Use the same (t.id || t._id) normalization here.

Copilot uses AI. Check for mistakes.
Comment thread src/lib/team-logos.ts
Comment on lines +28 to +32
export interface TeamLogo {
id: TeamLogoId;
icon: any;
label: string;
}
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TeamLogo.icon is typed as any, which defeats type safety and can hide invalid icon components. Prefer using the lucide-react icon type (e.g., LucideIcon/ComponentType) and tighten getLogoById to accept TeamLogoId to prevent accidental invalid IDs.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants