improved a lot of things
This commit is contained in:
@@ -4,8 +4,10 @@ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
|
|||||||
CLERK_SECRET_KEY=sk_test_...
|
CLERK_SECRET_KEY=sk_test_...
|
||||||
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
|
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
|
||||||
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
|
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
|
||||||
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/workspaces
|
# Force redirect after OAuth (GitHub, Google) — overrides any redirect_url param
|
||||||
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/onboarding
|
# "Force" always wins; "Fallback" only fires when no redirect_url param exists.
|
||||||
|
NEXT_PUBLIC_CLERK_SIGN_IN_FORCE_REDIRECT_URL=/workspaces
|
||||||
|
NEXT_PUBLIC_CLERK_SIGN_UP_FORCE_REDIRECT_URL=/onboarding
|
||||||
|
|
||||||
# ── Supabase ──────────────────────────────────────────────────────────────────
|
# ── Supabase ──────────────────────────────────────────────────────────────────
|
||||||
# https://supabase.com/dashboard → Project Settings → API
|
# https://supabase.com/dashboard → Project Settings → API
|
||||||
|
|||||||
@@ -12,7 +12,13 @@ export default function RootLayout({ children }: { children: ReactNode }) {
|
|||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body>
|
<body>
|
||||||
<ClerkProvider>
|
{/* signInForceRedirectUrl / signUpForceRedirectUrl override any redirect_url
|
||||||
|
param that OAuth providers (GitHub, Google) may inject — ensuring we
|
||||||
|
always land on the right page regardless of the OAuth callback URL. */}
|
||||||
|
<ClerkProvider
|
||||||
|
signInForceRedirectUrl="/workspaces"
|
||||||
|
signUpForceRedirectUrl="/onboarding"
|
||||||
|
>
|
||||||
<Providers>{children}</Providers>
|
<Providers>{children}</Providers>
|
||||||
</ClerkProvider>
|
</ClerkProvider>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { redirect } from 'next/navigation';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { serverClient } from '@/lib/supabase';
|
import { serverClient } from '@/lib/supabase';
|
||||||
import { AppHeader } from '@/components/shell/AppHeader';
|
import { AppHeader } from '@/components/shell/AppHeader';
|
||||||
|
import { ProjectCard } from '@/components/shell/ProjectCard';
|
||||||
import type { Workspace, Project } from '@originmain/origin-graph';
|
import type { Workspace, Project } from '@originmain/origin-graph';
|
||||||
|
|
||||||
export async function generateMetadata({ params }: { params: Promise<{ wid: string }> }) {
|
export async function generateMetadata({ params }: { params: Promise<{ wid: string }> }) {
|
||||||
@@ -12,15 +13,6 @@ export async function generateMetadata({ params }: { params: Promise<{ wid: stri
|
|||||||
return { title: `${result.data?.name ?? 'Workspace'} — Originmain` };
|
return { title: `${result.data?.name ?? 'Workspace'} — Originmain` };
|
||||||
}
|
}
|
||||||
|
|
||||||
const FRAMEWORK_ICON: Record<string, string> = {
|
|
||||||
next: '▲',
|
|
||||||
react: '⚛',
|
|
||||||
vue: 'V',
|
|
||||||
svelte: 'S',
|
|
||||||
angular: 'A',
|
|
||||||
nuxt: 'N',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default async function WorkspacePage({ params }: { params: Promise<{ wid: string }> }) {
|
export default async function WorkspacePage({ params }: { params: Promise<{ wid: string }> }) {
|
||||||
const { wid } = await params;
|
const { wid } = await params;
|
||||||
const { userId } = await auth();
|
const { userId } = await auth();
|
||||||
@@ -121,64 +113,17 @@ export default async function WorkspacePage({ params }: { params: Promise<{ wid:
|
|||||||
{/* Project cards */}
|
{/* Project cards */}
|
||||||
{projects.length > 0 && (
|
{projects.length > 0 && (
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 16 }}>
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 16 }}>
|
||||||
{projects.map(project => {
|
{projects.map(project => (
|
||||||
const icon = project.framework ? (FRAMEWORK_ICON[project.framework.toLowerCase()] ?? '◻') : '◻';
|
<ProjectCard
|
||||||
return (
|
key={project.id}
|
||||||
<Link key={project.id} href={`/workspace/${wid}/project/${project.id}`} style={{ textDecoration: 'none' }}>
|
workspaceId={wid}
|
||||||
<div style={{
|
projectId={project.id}
|
||||||
background: '#FFFFFF',
|
name={project.name}
|
||||||
border: '1px solid rgba(0,0,0,0.07)',
|
framework={project.framework}
|
||||||
borderRadius: 14, padding: '20px 22px', cursor: 'pointer',
|
appUrl={project.app_url}
|
||||||
transition: 'box-shadow 0.15s, border-color 0.15s',
|
description={project.description}
|
||||||
}}
|
/>
|
||||||
onMouseEnter={e => {
|
))}
|
||||||
(e.currentTarget as HTMLElement).style.boxShadow = '0 4px 16px rgba(0,0,0,0.08)';
|
|
||||||
(e.currentTarget as HTMLElement).style.borderColor = 'rgba(0,0,0,0.12)';
|
|
||||||
}}
|
|
||||||
onMouseLeave={e => {
|
|
||||||
(e.currentTarget as HTMLElement).style.boxShadow = 'none';
|
|
||||||
(e.currentTarget as HTMLElement).style.borderColor = 'rgba(0,0,0,0.07)';
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
|
|
||||||
<div style={{
|
|
||||||
width: 38, height: 38, borderRadius: 9,
|
|
||||||
background: '#0A0A0A',
|
|
||||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
||||||
fontSize: '0.875rem', color: '#FFFFFF', fontWeight: 700, flexShrink: 0,
|
|
||||||
}}>
|
|
||||||
{icon}
|
|
||||||
</div>
|
|
||||||
<span style={{ fontSize: '0.9375rem', fontWeight: 600, color: '#0A0A0A', letterSpacing: '-0.01em' }}>
|
|
||||||
{project.name}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{project.app_url && (
|
|
||||||
<p style={{
|
|
||||||
margin: '0 0 6px',
|
|
||||||
fontSize: '0.75rem', fontFamily: 'monospace',
|
|
||||||
color: '#71717A', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
|
|
||||||
}}>
|
|
||||||
{project.app_url}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{project.description && (
|
|
||||||
<p style={{ margin: '0 0 12px', fontSize: '0.8125rem', color: '#71717A', lineHeight: 1.5 }}>
|
|
||||||
{project.description}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div style={{ marginTop: 16, display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
||||||
<span style={{ fontSize: '0.8125rem', color: '#0066FF', fontWeight: 500 }}>
|
|
||||||
Open canvas →
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -3,16 +3,11 @@ import { redirect } from 'next/navigation';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { serverClient } from '@/lib/supabase';
|
import { serverClient } from '@/lib/supabase';
|
||||||
import { AppHeader } from '@/components/shell/AppHeader';
|
import { AppHeader } from '@/components/shell/AppHeader';
|
||||||
|
import { WorkspaceCard } from '@/components/shell/WorkspaceCard';
|
||||||
import type { Workspace } from '@originmain/origin-graph';
|
import type { Workspace } from '@originmain/origin-graph';
|
||||||
|
|
||||||
export const metadata = { title: 'Workspaces — Originmain' };
|
export const metadata = { title: 'Workspaces — Originmain' };
|
||||||
|
|
||||||
const PLAN_BADGE: Record<string, { label: string; bg: string; color: string }> = {
|
|
||||||
FREE: { label: 'Free', bg: 'rgba(0,0,0,0.05)', color: '#52525B' },
|
|
||||||
TEAM: { label: 'Team', bg: 'rgba(0,102,255,0.08)', color: '#0066FF' },
|
|
||||||
ENTERPRISE: { label: 'Enterprise', bg: 'rgba(124,58,237,0.08)', color: '#7C3AED' },
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getWorkspaces(userId: string): Promise<Workspace[]> {
|
async function getWorkspaces(userId: string): Promise<Workspace[]> {
|
||||||
const db = serverClient();
|
const db = serverClient();
|
||||||
const { data: memberships } = await db
|
const { data: memberships } = await db
|
||||||
@@ -62,68 +57,15 @@ export default async function WorkspacesPage() {
|
|||||||
|
|
||||||
{/* Workspace cards */}
|
{/* Workspace cards */}
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 16 }}>
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 16 }}>
|
||||||
{workspaces.map(ws => {
|
{workspaces.map(ws => (
|
||||||
const badge = PLAN_BADGE[ws.plan] ?? PLAN_BADGE['FREE']!;
|
<WorkspaceCard
|
||||||
return (
|
key={ws.id}
|
||||||
<Link key={ws.id} href={`/workspace/${ws.id}`} style={{ textDecoration: 'none' }}>
|
id={ws.id}
|
||||||
<div style={{
|
name={ws.name}
|
||||||
background: '#FFFFFF',
|
plan={ws.plan}
|
||||||
border: '1px solid rgba(0,0,0,0.07)',
|
createdAt={ws.created_at}
|
||||||
borderRadius: 14,
|
/>
|
||||||
padding: '20px 22px',
|
))}
|
||||||
cursor: 'pointer',
|
|
||||||
transition: 'box-shadow 0.15s, border-color 0.15s',
|
|
||||||
}}
|
|
||||||
onMouseEnter={e => {
|
|
||||||
(e.currentTarget as HTMLElement).style.boxShadow = '0 4px 16px rgba(0,0,0,0.08)';
|
|
||||||
(e.currentTarget as HTMLElement).style.borderColor = 'rgba(0,0,0,0.12)';
|
|
||||||
}}
|
|
||||||
onMouseLeave={e => {
|
|
||||||
(e.currentTarget as HTMLElement).style.boxShadow = 'none';
|
|
||||||
(e.currentTarget as HTMLElement).style.borderColor = 'rgba(0,0,0,0.07)';
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* Workspace icon */}
|
|
||||||
<div style={{
|
|
||||||
width: 40, height: 40, borderRadius: 10,
|
|
||||||
background: 'rgba(0,102,255,0.07)', border: '1px solid rgba(0,102,255,0.15)',
|
|
||||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
||||||
marginBottom: 14,
|
|
||||||
}}>
|
|
||||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
|
|
||||||
<rect x="1.5" y="1.5" width="6.5" height="6.5" rx="1.5" fill="#0066FF" opacity="0.7"/>
|
|
||||||
<rect x="10" y="1.5" width="6.5" height="6.5" rx="1.5" fill="#0066FF" opacity="0.4"/>
|
|
||||||
<rect x="1.5" y="10" width="6.5" height="6.5" rx="1.5" fill="#0066FF" opacity="0.4"/>
|
|
||||||
<rect x="10" y="10" width="6.5" height="6.5" rx="1.5" fill="#0066FF" opacity="0.2"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 8 }}>
|
|
||||||
<span style={{ fontSize: '0.9375rem', fontWeight: 600, color: '#0A0A0A', letterSpacing: '-0.01em', lineHeight: 1.3 }}>
|
|
||||||
{ws.name}
|
|
||||||
</span>
|
|
||||||
<span style={{
|
|
||||||
fontSize: '0.6875rem', fontWeight: 600, letterSpacing: '0.04em',
|
|
||||||
textTransform: 'uppercase', padding: '2px 8px', borderRadius: 99,
|
|
||||||
background: badge.bg, color: badge.color, flexShrink: 0,
|
|
||||||
}}>
|
|
||||||
{badge.label}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p style={{ margin: '6px 0 0', fontSize: '0.8125rem', color: '#A1A1AA' }}>
|
|
||||||
Created {new Date(ws.created_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div style={{ marginTop: 16, display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
||||||
<span style={{ fontSize: '0.8125rem', color: '#0066FF', fontWeight: 500 }}>
|
|
||||||
Open workspace →
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
interface ProjectCardProps {
|
||||||
|
workspaceId: string;
|
||||||
|
projectId: string;
|
||||||
|
name: string;
|
||||||
|
framework?: string | null;
|
||||||
|
appUrl?: string | null;
|
||||||
|
description?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FRAMEWORK_ICON: Record<string, string> = {
|
||||||
|
next: '▲',
|
||||||
|
react: '⚛',
|
||||||
|
vue: 'V',
|
||||||
|
svelte: 'S',
|
||||||
|
angular: 'A',
|
||||||
|
nuxt: 'N',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ProjectCard({ workspaceId, projectId, name, framework, appUrl, description }: ProjectCardProps) {
|
||||||
|
const icon = framework ? (FRAMEWORK_ICON[framework.toLowerCase()] ?? '◻') : '◻';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link href={`/workspace/${workspaceId}/project/${projectId}`} style={{ textDecoration: 'none' }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
background: '#FFFFFF',
|
||||||
|
border: '1px solid rgba(0,0,0,0.07)',
|
||||||
|
borderRadius: 14, padding: '20px 22px', cursor: 'pointer',
|
||||||
|
transition: 'box-shadow 0.15s, border-color 0.15s',
|
||||||
|
}}
|
||||||
|
onMouseEnter={e => {
|
||||||
|
(e.currentTarget as HTMLElement).style.boxShadow = '0 4px 16px rgba(0,0,0,0.08)';
|
||||||
|
(e.currentTarget as HTMLElement).style.borderColor = 'rgba(0,0,0,0.12)';
|
||||||
|
}}
|
||||||
|
onMouseLeave={e => {
|
||||||
|
(e.currentTarget as HTMLElement).style.boxShadow = 'none';
|
||||||
|
(e.currentTarget as HTMLElement).style.borderColor = 'rgba(0,0,0,0.07)';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
|
||||||
|
<div style={{
|
||||||
|
width: 38, height: 38, borderRadius: 9,
|
||||||
|
background: '#0A0A0A',
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
fontSize: '0.875rem', color: '#FFFFFF', fontWeight: 700, flexShrink: 0,
|
||||||
|
}}>
|
||||||
|
{icon}
|
||||||
|
</div>
|
||||||
|
<span style={{ fontSize: '0.9375rem', fontWeight: 600, color: '#0A0A0A', letterSpacing: '-0.01em' }}>
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{appUrl && (
|
||||||
|
<p style={{
|
||||||
|
margin: '0 0 6px',
|
||||||
|
fontSize: '0.75rem', fontFamily: 'monospace',
|
||||||
|
color: '#71717A', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
|
||||||
|
}}>
|
||||||
|
{appUrl}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{description && (
|
||||||
|
<p style={{ margin: '0 0 12px', fontSize: '0.8125rem', color: '#71717A', lineHeight: 1.5 }}>
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div style={{ marginTop: 16, display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
|
<span style={{ fontSize: '0.8125rem', color: '#0066FF', fontWeight: 500 }}>
|
||||||
|
Open canvas →
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
interface WorkspaceCardProps {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
plan: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PLAN_BADGE: Record<string, { label: string; bg: string; color: string }> = {
|
||||||
|
FREE: { label: 'Free', bg: 'rgba(0,0,0,0.05)', color: '#52525B' },
|
||||||
|
TEAM: { label: 'Team', bg: 'rgba(0,102,255,0.08)', color: '#0066FF' },
|
||||||
|
ENTERPRISE: { label: 'Enterprise', bg: 'rgba(124,58,237,0.08)', color: '#7C3AED' },
|
||||||
|
};
|
||||||
|
|
||||||
|
export function WorkspaceCard({ id, name, plan, createdAt }: WorkspaceCardProps) {
|
||||||
|
const badge = PLAN_BADGE[plan] ?? PLAN_BADGE['FREE']!;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link href={`/workspace/${id}`} style={{ textDecoration: 'none' }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
background: '#FFFFFF',
|
||||||
|
border: '1px solid rgba(0,0,0,0.07)',
|
||||||
|
borderRadius: 14,
|
||||||
|
padding: '20px 22px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
transition: 'box-shadow 0.15s, border-color 0.15s',
|
||||||
|
}}
|
||||||
|
onMouseEnter={e => {
|
||||||
|
(e.currentTarget as HTMLElement).style.boxShadow = '0 4px 16px rgba(0,0,0,0.08)';
|
||||||
|
(e.currentTarget as HTMLElement).style.borderColor = 'rgba(0,0,0,0.12)';
|
||||||
|
}}
|
||||||
|
onMouseLeave={e => {
|
||||||
|
(e.currentTarget as HTMLElement).style.boxShadow = 'none';
|
||||||
|
(e.currentTarget as HTMLElement).style.borderColor = 'rgba(0,0,0,0.07)';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Workspace icon */}
|
||||||
|
<div style={{
|
||||||
|
width: 40, height: 40, borderRadius: 10,
|
||||||
|
background: 'rgba(0,102,255,0.07)', border: '1px solid rgba(0,102,255,0.15)',
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
marginBottom: 14,
|
||||||
|
}}>
|
||||||
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
|
||||||
|
<rect x="1.5" y="1.5" width="6.5" height="6.5" rx="1.5" fill="#0066FF" opacity="0.7"/>
|
||||||
|
<rect x="10" y="1.5" width="6.5" height="6.5" rx="1.5" fill="#0066FF" opacity="0.4"/>
|
||||||
|
<rect x="1.5" y="10" width="6.5" height="6.5" rx="1.5" fill="#0066FF" opacity="0.4"/>
|
||||||
|
<rect x="10" y="10" width="6.5" height="6.5" rx="1.5" fill="#0066FF" opacity="0.2"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 8 }}>
|
||||||
|
<span style={{ fontSize: '0.9375rem', fontWeight: 600, color: '#0A0A0A', letterSpacing: '-0.01em', lineHeight: 1.3 }}>
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
|
<span style={{
|
||||||
|
fontSize: '0.6875rem', fontWeight: 600, letterSpacing: '0.04em',
|
||||||
|
textTransform: 'uppercase', padding: '2px 8px', borderRadius: 99,
|
||||||
|
background: badge.bg, color: badge.color, flexShrink: 0,
|
||||||
|
}}>
|
||||||
|
{badge.label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p style={{ margin: '6px 0 0', fontSize: '0.8125rem', color: '#A1A1AA' }}>
|
||||||
|
Created {new Date(createdAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div style={{ marginTop: 16, display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
|
<span style={{ fontSize: '0.8125rem', color: '#0066FF', fontWeight: 500 }}>
|
||||||
|
Open workspace →
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user