feat: add approvals workflow and unified member invitation management across projects, workspaces, and videos

This commit is contained in:
Yusuf İpek
2026-02-25 16:24:45 +03:00
parent 0ae1becc1b
commit a2b07b3e19
36 changed files with 3122 additions and 982 deletions
@@ -18,15 +18,17 @@ interface DashboardClientProps {
serializedProjects: SerializedProject[];
workspaces: { id: string; name: string }[];
totalPages: number;
canCreateProjects: boolean;
}
export function DashboardClient({ serializedProjects, workspaces, totalPages }: DashboardClientProps) {
export function DashboardClient({ serializedProjects, workspaces, totalPages, canCreateProjects }: DashboardClientProps) {
return (
<div className="px-6 lg:px-8 py-8 w-full">
<ProjectFilter
projects={serializedProjects}
workspaces={workspaces}
totalPages={totalPages}
canCreateProjects={canCreateProjects}
/>
</div>
);
+11
View File
@@ -49,6 +49,16 @@ export default async function DashboardPage({
distinct: ['workspaceId']
});
const creatableWorkspaces = await db.workspace.count({
where: {
OR: [
{ ownerId: session.user.id },
{ members: { some: { userId: session.user.id, role: 'ADMIN' } } },
],
},
});
const canCreateProjects = creatableWorkspaces > 0;
const workspaceMap = new Map<string, string>();
for (const project of accessibleProjects) {
if (project.workspace) {
@@ -105,6 +115,7 @@ export default async function DashboardPage({
serializedProjects={serializedProjects}
workspaces={workspaces}
totalPages={totalPages}
canCreateProjects={canCreateProjects}
/>
);
}
+18 -13
View File
@@ -31,6 +31,7 @@ interface ProjectFilterProps {
projects: SerializedProject[];
workspaces: { id: string; name: string }[];
totalPages: number;
canCreateProjects: boolean;
}
function formatRelativeTime(dateStr: string): string {
@@ -61,7 +62,7 @@ function VisibilityIcon({ visibility }: { visibility: string }) {
type SortOrder = 'desc' | 'asc';
export function ProjectFilter({ projects, workspaces, totalPages }: ProjectFilterProps) {
export function ProjectFilter({ projects, workspaces, totalPages, canCreateProjects }: ProjectFilterProps) {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
@@ -138,12 +139,14 @@ export function ProjectFilter({ projects, workspaces, totalPages }: ProjectFilte
</>
)}
</Button>
<Button asChild>
<Link href="/projects/new">
<Plus className="h-4 w-4 mr-2" />
New Project
</Link>
</Button>
{canCreateProjects && (
<Button asChild>
<Link href="/projects/new">
<Plus className="h-4 w-4 mr-2" />
New Project
</Link>
</Button>
)}
</div>
</div>
@@ -205,12 +208,14 @@ export function ProjectFilter({ projects, workspaces, totalPages }: ProjectFilte
? 'Create your first project to start collecting video feedback'
: 'Create a project in this workspace to get started'}
</p>
<Button asChild>
<Link href="/projects/new">
<Plus className="h-4 w-4 mr-2" />
Create Project
</Link>
</Button>
{canCreateProjects && (
<Button asChild>
<Link href="/projects/new">
<Plus className="h-4 w-4 mr-2" />
Create Project
</Link>
</Button>
)}
</CardContent>
</Card>
)}