Function bodies 345 total
getTenantFromCustomDomain function · typescript · L13-L24 (12 LOC)src/app/cd/privacy/page.tsx
async function getTenantFromCustomDomain() {
const headersList = await headers();
const customDomain = headersList.get('x-custom-domain');
if (!customDomain) return null;
await connectDB();
return await Tenant.findOne({
customDomain: customDomain.toLowerCase(),
customDomainStatus: 'verified',
status: 'active',
}).lean();
}generateMetadata function · typescript · L26-L38 (13 LOC)src/app/cd/privacy/page.tsx
export async function generateMetadata(): Promise<Metadata> {
const tenant = await getTenantFromCustomDomain();
if (!tenant) return {};
await connectDB();
const settings = await Settings.findOne({ tenantId: tenant._id }).lean();
const siteName = settings?.siteName || tenant.settings?.siteName || tenant.name;
return {
title: `Privacy Policy | ${siteName}`,
description: `Privacy Policy for ${siteName}`,
};
}PrivacyPage function · typescript · L40-L156 (117 LOC)src/app/cd/privacy/page.tsx
export default async function PrivacyPage() {
const tenant = await getTenantFromCustomDomain();
if (!tenant) {
notFound();
}
await connectDB();
const settings = await Settings.findOne({ tenantId: tenant._id }).lean();
const siteName = settings?.siteName || tenant.settings?.siteName || tenant.name;
const siteLogo = settings?.siteLogo || '';
const primaryColor = settings?.primaryColor || '#6366f1';
const accentColor = settings?.accentColor || '#8b5cf6';
const privacyPolicy = settings?.privacyPolicy || '';
// Footer settings
const copyrightText = (settings?.copyrightText || '© {year} All rights reserved.')
.replace('{year}', new Date().getFullYear().toString());
const socialLinks = (settings?.socialLinks || {}) as any;
const contactEmail = settings?.contactEmail || '';
const contactPhone = settings?.contactPhone || '';
const contactAddress = settings?.contactAddress || '';
const footerLinks = (settings?.footerLinks || []) as Array<{
id: strgetTenantFromCustomDomain function · typescript · L13-L24 (12 LOC)src/app/cd/terms/page.tsx
async function getTenantFromCustomDomain() {
const headersList = await headers();
const customDomain = headersList.get('x-custom-domain');
if (!customDomain) return null;
await connectDB();
return await Tenant.findOne({
customDomain: customDomain.toLowerCase(),
customDomainStatus: 'verified',
status: 'active',
}).lean();
}generateMetadata function · typescript · L26-L38 (13 LOC)src/app/cd/terms/page.tsx
export async function generateMetadata(): Promise<Metadata> {
const tenant = await getTenantFromCustomDomain();
if (!tenant) return {};
await connectDB();
const settings = await Settings.findOne({ tenantId: tenant._id }).lean();
const siteName = settings?.siteName || tenant.settings?.siteName || tenant.name;
return {
title: `Terms of Service | ${siteName}`,
description: `Terms of Service for ${siteName}`,
};
}TermsPage function · typescript · L40-L156 (117 LOC)src/app/cd/terms/page.tsx
export default async function TermsPage() {
const tenant = await getTenantFromCustomDomain();
if (!tenant) {
notFound();
}
await connectDB();
const settings = await Settings.findOne({ tenantId: tenant._id }).lean();
const siteName = settings?.siteName || tenant.settings?.siteName || tenant.name;
const siteLogo = settings?.siteLogo || '';
const primaryColor = settings?.primaryColor || '#6366f1';
const accentColor = settings?.accentColor || '#8b5cf6';
const termsOfService = settings?.termsOfService || '';
// Footer settings
const copyrightText = (settings?.copyrightText || '© {year} All rights reserved.')
.replace('{year}', new Date().getFullYear().toString());
const socialLinks = (settings?.socialLinks || {}) as any;
const contactEmail = settings?.contactEmail || '';
const contactPhone = settings?.contactPhone || '';
const contactAddress = settings?.contactAddress || '';
const footerLinks = (settings?.footerLinks || []) as Array<{
id: strContactPage function · typescript · L5-L171 (167 LOC)src/app/contact/page.tsx
export default function ContactPage() {
const [formData, setFormData] = useState({
name: '',
email: '',
subject: '',
message: '',
});
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
const [errorMessage, setErrorMessage] = useState('');
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
setStatus('loading');
setErrorMessage('');
try {
const response = await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Failed to send message');
}
setStatus('success');
setFormData({ name: '', email: '', subject: '', message: '' });
} catch (error: any) {
setStatus('error');
setErrorMessage(error.message || 'Something went wrong');
}
};
return All rows scored by the Repobility analyzer (https://repobility.com)
RootLayout function · typescript · L55-L72 (18 LOC)src/app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<link rel="icon" href="/favicon.ico" sizes="any" />
</head>
<body className={inter.className}>
<SessionProvider>
<LayoutWrapper>{children}</LayoutWrapper>
</SessionProvider>
</body>
</html>
);
}LoginPage function · typescript · L9-L359 (351 LOC)src/app/login/page.tsx
export default function LoginPage() {
const router = useRouter();
const { data: session, status } = useSession();
const [isFormLoading, setIsFormLoading] = useState(false);
const [isGoogleLoading, setIsGoogleLoading] = useState(false);
const [error, setError] = useState('');
const [loginMode, setLoginMode] = useState<'owner' | 'author'>('owner');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
// If user is logged in, redirect to their blog or dashboard
useEffect(() => {
if (status === 'authenticated' && session?.user) {
// Check if user has a tenant
checkUserTenant();
}
}, [status, session]);
const checkUserTenant = async () => {
try {
const res = await fetch('/api/user/tenant');
const data = await res.json();
if (data.success && data.data) {
// Redirect to their tenant admin
router.push(`/t/${data.data.slug}/admin`);
} else {
// No tenant, redirect MarketingLayout function · typescript · L5-L109 (105 LOC)src/app/(marketing)/layout.tsx
export default async function MarketingLayout({
children,
}: {
children: React.ReactNode;
}) {
// Check if this is a tenant subdomain - if so, redirect to tenant site
const headersList = await headers();
const tenantType = headersList.get('x-tenant-type');
if (tenantType === 'tenant') {
// This is a tenant subdomain, shouldn't show marketing pages
redirect('/');
}
return (
<div className="min-h-screen bg-white dark:bg-gray-950">
{/* Navigation */}
<nav className="fixed top-0 left-0 right-0 z-50 bg-white/80 dark:bg-gray-950/80 backdrop-blur-xl border-b border-gray-200 dark:border-gray-800">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
{/* Logo */}
<Link href="/" className="flex items-center gap-2">
<div className="w-8 h-8 bg-gradient-to-br from-indigo-500 to-purple-600 rounded-lg flex items-center justify-center">
HomePage function · typescript · L4-L270 (267 LOC)src/app/(marketing)/page.tsx
export default function HomePage() {
return (
<>
{/* Hero Section */}
<section className="relative overflow-hidden">
{/* Background gradient */}
<div className="absolute inset-0 bg-gradient-to-br from-indigo-50 via-white to-purple-50 dark:from-gray-950 dark:via-gray-900 dark:to-gray-950" />
<div className="absolute inset-0 bg-[linear-gradient(to_right,#8080800a_1px,transparent_1px),linear-gradient(to_bottom,#8080800a_1px,transparent_1px)] bg-[size:24px_24px]" />
{/* Floating shapes */}
<div className="absolute top-20 left-10 w-72 h-72 bg-indigo-500/10 rounded-full blur-3xl" />
<div className="absolute bottom-20 right-10 w-96 h-96 bg-purple-500/10 rounded-full blur-3xl" />
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-24 md:py-32">
<div className="text-center max-w-4xl mx-auto">
<div className="inline-flex items-center gap-2 px-4 py-2 bg-indigo-50 dark:bg-indigo-500/10 PricingPage function · typescript · L81-L276 (196 LOC)src/app/(marketing)/pricing/page.tsx
export default function PricingPage() {
return (
<div className="py-16">
{/* Header */}
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center mb-16">
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 dark:text-white mb-4">
Simple, transparent pricing
</h1>
<p className="text-xl text-gray-600 dark:text-gray-400 max-w-2xl mx-auto">
Choose the perfect plan for your blogging needs. Start free and upgrade anytime.
</p>
</div>
{/* Pricing Cards */}
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
{plans.map((plan) => (
<div
key={plan.slug}
className={`relative rounded-2xl p-6 ${
plan.popular
? 'bg-indigo-600 text-white ring-4 ring-indigo-600 ring-offset-2 dark:ring-offset-gray-950'
: 'bg-white dark:bg-gray-8CompleteRegistrationPage function · typescript · L22-L209 (188 LOC)src/app/(marketing)/register/complete/page.tsx
export default function CompleteRegistrationPage() {
const router = useRouter();
const { data: session, status } = useSession();
const [state, setState] = useState<'loading' | 'creating' | 'success' | 'error' | 'no-data'>('loading');
const [error, setError] = useState('');
const [subdomain, setSubdomain] = useState('');
useEffect(() => {
// Wait for session to load
if (status === 'loading') return;
// If not authenticated, redirect to register
if (status === 'unauthenticated') {
router.push('/register');
return;
}
// Get pending registration data from localStorage
const pendingData = localStorage.getItem('pendingRegistration');
if (!pendingData) {
setState('no-data');
return;
}
const registrationData: PendingRegistration = JSON.parse(pendingData);
// Validate data
if (!registrationData.plan || !registrationData.blogName || !registrationData.slug) {
setState('no-data');
return;
}
fetchPlans function · typescript · L365-L383 (19 LOC)src/app/(marketing)/register/page.tsx
async function fetchPlans() {
try {
const res = await fetch('/api/plans');
const data = await res.json();
if (data.success) {
setPlans(data.data);
if (!preselectedPlan && data.data.length > 0) {
const freePlan = data.data.find((p: Plan) => p.slug === 'free');
if (freePlan) {
setSelectedPlan('free');
}
}
}
} catch (err) {
console.error('Error fetching plans:', err);
} finally {
setLoadingPlans(false);
}
}LoadingFallback function · typescript · L1040-L1049 (10 LOC)src/app/(marketing)/register/page.tsx
function LoadingFallback() {
return (
<div className="min-h-[calc(100vh-4rem)] flex items-center justify-center py-12 px-4">
<div className="text-center">
<Loader2 className="w-8 h-8 animate-spin text-indigo-600 mx-auto mb-4" />
<p className="text-gray-600 dark:text-gray-400">Loading...</p>
</div>
</div>
);
}Repobility · open methodology · https://repobility.com/research/
RegisterPage function · typescript · L1051-L1057 (7 LOC)src/app/(marketing)/register/page.tsx
export default function RegisterPage() {
return (
<Suspense fallback={<LoadingFallback />}>
<RegisterPageContent />
</Suspense>
);
}RegisterSuccessContent function · typescript · L8-L128 (121 LOC)src/app/(marketing)/register/success/page.tsx
function RegisterSuccessContent() {
const searchParams = useSearchParams();
const subdomain = searchParams.get('subdomain') || 'yourblog.launchory.org';
const planRequest = searchParams.get('planRequest');
const requestedPlan = searchParams.get('requestedPlan');
// Extract slug from subdomain (e.g., "myblog.launchory.org" -> "myblog")
const slug = subdomain.split('.')[0];
// Use internal URL format for development compatibility
const blogUrl = `/t/${slug}`;
const adminUrl = `/t/${slug}/admin`;
return (
<div className="min-h-[calc(100vh-4rem)] flex items-center justify-center py-12 px-4">
<div className="w-full max-w-md text-center">
{/* Success Icon */}
<div className="w-20 h-20 bg-green-100 dark:bg-green-900/30 rounded-full flex items-center justify-center mx-auto mb-8">
<CheckCircle className="w-10 h-10 text-green-600 dark:text-green-400" />
</div>
<h1 className="text-3xl font-bold text-gray-900 dark:text-whiLoadingFallback function · typescript · L130-L139 (10 LOC)src/app/(marketing)/register/success/page.tsx
function LoadingFallback() {
return (
<div className="min-h-[calc(100vh-4rem)] flex items-center justify-center py-12 px-4">
<div className="text-center">
<div className="h-8 w-8 animate-spin rounded-full border-4 border-indigo-600 border-t-transparent mx-auto mb-4" />
<p className="text-gray-600 dark:text-gray-400">Loading...</p>
</div>
</div>
);
}RegisterSuccessPage function · typescript · L141-L147 (7 LOC)src/app/(marketing)/register/success/page.tsx
export default function RegisterSuccessPage() {
return (
<Suspense fallback={<LoadingFallback />}>
<RegisterSuccessContent />
</Suspense>
);
}NotFound function · typescript · L3-L29 (27 LOC)src/app/not-found.tsx
export default function NotFound() {
return (
<div className="container mx-auto flex min-h-[60vh] flex-col items-center justify-center px-4 py-12 text-center">
<h1 className="mb-4 text-6xl font-bold text-gray-900 dark:text-white">404</h1>
<h2 className="mb-4 text-2xl font-semibold text-gray-700 dark:text-gray-300">
Page Not Found
</h2>
<p className="mb-8 max-w-md text-gray-600 dark:text-gray-400">
Sorry, the page you are looking for doesn't exist or has been moved.
</p>
<div className="flex gap-4">
<Link
href="/"
className="rounded-lg bg-primary-600 px-6 py-3 font-medium text-white transition-colors hover:bg-primary-700"
>
Go Home
</Link>
<Link
href="/blog"
className="rounded-lg border border-gray-300 px-6 py-3 font-medium text-gray-700 transition-colors hover:bg-gray-100 dark:border-gray-700 dark:text-gray-300 dark:hover:bg-gray-800"
getStats function · typescript · L9-L20 (12 LOC)src/app/page.tsx
async function getStats() {
try {
await connectDB();
const [tenantsCount, plansData] = await Promise.all([
Tenant.countDocuments({ status: 'active' }),
Plan.find({ isActive: true }).sort({ sortOrder: 1 }).lean(),
]);
return { tenantsCount, plans: plansData };
} catch (error) {
return { tenantsCount: 0, plans: [] };
}
}MarketingHomePage function · typescript · L22-L395 (374 LOC)src/app/page.tsx
export default async function MarketingHomePage() {
const { tenantsCount, plans } = await getStats();
return (
<main className="min-h-screen bg-white dark:bg-gray-950">
{/* Navigation */}
<nav className="fixed top-0 left-0 right-0 z-50 border-b border-gray-200/50 dark:border-white/10 bg-white/80 dark:bg-gray-950/80 backdrop-blur-xl">
<div className="container mx-auto px-4">
<div className="flex items-center justify-between h-16">
<Link href="/" className="flex items-center gap-2">
<div className="w-8 h-8 bg-gradient-to-br from-indigo-500 to-purple-600 rounded-lg flex items-center justify-center">
<svg className="w-5 h-5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
<span className="text-xl font-bold text-gray-90robots function · typescript · L4-L13 (10 LOC)src/app/robots.ts
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: '*',
allow: '/',
disallow: ['/api/', '/admin/'],
},
sitemap: `${siteConfig.url}/sitemap.xml`,
};
}Repobility · code-quality intelligence · https://repobility.com
SearchPage function · typescript · L15-L49 (35 LOC)src/app/search/page.tsx
export default async function SearchPage({ searchParams }: SearchPageProps) {
const { q: query } = await searchParams;
const posts = query ? await searchPosts(query) : [];
return (
<div className="container mx-auto px-4 py-12">
{/* Header */}
<div className="mb-12 text-center">
<h1 className="mb-4 text-4xl font-bold text-gray-900 dark:text-white">
Search
</h1>
<p className="mb-6 text-lg text-gray-600 dark:text-gray-400">
Find articles by title, description, or tags
</p>
<div className="mx-auto max-w-md">
<SearchBar />
</div>
</div>
{/* Results */}
{query ? (
<div>
<p className="mb-8 text-center text-gray-600 dark:text-gray-400">
{posts.length} {posts.length === 1 ? 'result' : 'results'} for "{query}"
</p>
<PostList posts={posts} />
</div>
) : (
<p className="text-center text-gray-500 darSignupContent function · typescript · L6-L25 (20 LOC)src/app/signup/page.tsx
function SignupContent() {
const router = useRouter();
const searchParams = useSearchParams();
const plan = searchParams.get('plan');
useEffect(() => {
// Redirect to register page with plan param if provided
const redirectUrl = plan ? `/register?plan=${plan}` : '/register';
router.replace(redirectUrl);
}, [router, plan]);
return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center">
<div className="text-center">
<div className="h-8 w-8 animate-spin rounded-full border-4 border-indigo-600 border-t-transparent mx-auto mb-4" />
<p className="text-gray-600 dark:text-gray-400">Redirecting...</p>
</div>
</div>
);
}LoadingFallback function · typescript · L27-L36 (10 LOC)src/app/signup/page.tsx
function LoadingFallback() {
return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center">
<div className="text-center">
<div className="h-8 w-8 animate-spin rounded-full border-4 border-indigo-600 border-t-transparent mx-auto mb-4" />
<p className="text-gray-600 dark:text-gray-400">Loading...</p>
</div>
</div>
);
}SignupPage function · typescript · L39-L45 (7 LOC)src/app/signup/page.tsx
export default function SignupPage() {
return (
<Suspense fallback={<LoadingFallback />}>
<SignupContent />
</Suspense>
);
}sitemap function · typescript · L6-L42 (37 LOC)src/app/sitemap.ts
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = siteConfig.url;
// Static pages
const staticPages = [
'',
'/blog',
'/authors',
'/about',
'/contact',
].map((route) => ({
url: `${baseUrl}${route}`,
lastModified: new Date(),
changeFrequency: 'weekly' as const,
priority: route === '' ? 1 : 0.8,
}));
// Blog posts
const posts = await getAllPosts();
const postPages = posts.map((post) => ({
url: `${baseUrl}/blog/${post.slug}`,
lastModified: new Date(post.date),
changeFrequency: 'monthly' as const,
priority: 0.6,
}));
// Author pages
const authors = await getAllAuthors();
const authorPages = authors.map((author) => ({
url: `${baseUrl}/authors/${author.slug}`,
lastModified: new Date(),
changeFrequency: 'monthly' as const,
priority: 0.5,
}));
return [...staticPages, ...postPages, ...authorPages];
}SuperAdminAuthorsPage function · typescript · L54-L405 (352 LOC)src/app/(super-admin)/super-admin/authors/page.tsx
export default function SuperAdminAuthorsPage() {
const [authors, setAuthors] = useState<Author[]>([]);
const [pagination, setPagination] = useState<Pagination>({
page: 1,
limit: 20,
total: 0,
totalPages: 0,
});
const [loading, setLoading] = useState(true);
const [search, setSearch] = useState('');
const [filters, setFilters] = useState({
canLogin: '',
canPost: '',
});
const [showFilters, setShowFilters] = useState(false);
useEffect(() => {
fetchAuthors();
}, [pagination.page, filters]);
const fetchAuthors = async () => {
setLoading(true);
try {
const params = new URLSearchParams({
page: pagination.page.toString(),
limit: pagination.limit.toString(),
});
if (search) params.set('search', search);
if (filters.canLogin) params.set('canLogin', filters.canLogin);
if (filters.canPost) params.set('canPost', filters.canPost);
const res = await fetch(`/api/super-admin/authors?${paraImpersonatePage function · typescript · L29-L221 (193 LOC)src/app/(super-admin)/super-admin/impersonate/[id]/page.tsx
export default function ImpersonatePage() {
const params = useParams();
const id = params.id as string;
const [data, setData] = useState<ImpersonationData | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [countdown, setCountdown] = useState(5);
const [autoRedirect, setAutoRedirect] = useState(false);
const initiateImpersonation = async () => {
try {
setLoading(true);
setError('');
const res = await fetch(`/api/super-admin/impersonate/${id}`, {
method: 'POST',
});
const responseData = await res.json();
if (!res.ok) {
throw new Error(responseData.error || 'Failed to create impersonation session');
}
setData(responseData.data);
} catch (err: any) {
setError(err.message);
} finally {
setLoading(false);
}
};
useEffect(() => {
if (autoRedirect && data && countdown > 0) {
const timer = setTimeout(() => {
SuperAdminLayout function · typescript · L54-L312 (259 LOC)src/app/(super-admin)/super-admin/layout.tsx
export default function SuperAdminLayout({
children,
}: {
children: React.ReactNode;
}) {
const router = useRouter();
const pathname = usePathname();
const [session, setSession] = useState<SuperAdminSession | null>(null);
const [loading, setLoading] = useState(true);
const [sidebarOpen, setSidebarOpen] = useState(false);
const [badges, setBadges] = useState<Record<string, number>>({});
// Use refs to cache session check timestamp
const lastSessionCheck = useRef<number>(0);
const sessionChecked = useRef<boolean>(false);
// Skip auth check for login page
const isLoginPage = pathname === '/super-admin/login';
// Fetch badge counts (pending plan requests)
const fetchBadgeCounts = useCallback(async () => {
try {
const res = await fetch('/api/super-admin/plan-requests?status=pending&limit=1');
const data = await res.json();
if (data.success) {
setBadges({
pendingRequests: data.data.stats?.pending || 0,
});
Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
SuperAdminLoginPage function · typescript · L7-L131 (125 LOC)src/app/(super-admin)/super-admin/login/page.tsx
export default function SuperAdminLoginPage() {
const router = useRouter();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError('');
try {
const res = await fetch('/api/super-admin/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || 'Login failed');
}
// Redirect to dashboard
router.push('/super-admin');
router.refresh();
} catch (err: any) {
setError(err.message);
} finally {
setLoading(false);
}
};
return (
<div classStatCard function · typescript · L73-L122 (50 LOC)src/app/(super-admin)/super-admin/page.tsx
function StatCard({
title,
value,
icon: Icon,
change,
changeLabel,
gradient,
link,
}: {
title: string;
value: number | string;
icon: any;
change?: number;
changeLabel?: string;
gradient: string;
link?: string;
}) {
const content = (
<div className={`relative overflow-hidden rounded-2xl p-6 ${gradient}`}>
<div className="relative z-10">
<div className="flex items-center justify-between">
<p className="text-sm font-medium text-white/80">{title}</p>
<div className="rounded-xl bg-white/20 p-2">
<Icon className="h-5 w-5 text-white" />
</div>
</div>
<p className="mt-3 text-3xl font-bold text-white">{value.toLocaleString()}</p>
{(change !== undefined || changeLabel) && (
<div className="mt-3 flex items-center gap-2">
{change !== undefined && (
<span className={`flex items-center text-sm font-medium ${change >= 0 ? 'text-emerald-200' : 'text-red-GrowthIndicator function · typescript · L124-L143 (20 LOC)src/app/(super-admin)/super-admin/page.tsx
function GrowthIndicator({ value, label }: { value: number; label: string }) {
const isPositive = value >= 0;
return (
<div className="flex items-center gap-3 rounded-xl bg-gray-50 dark:bg-gray-800/50 p-4">
<div className={`rounded-full p-2 ${isPositive ? 'bg-emerald-100 dark:bg-emerald-900/30' : 'bg-red-100 dark:bg-red-900/30'}`}>
{isPositive ? (
<TrendingUp className={`h-4 w-4 ${isPositive ? 'text-emerald-600 dark:text-emerald-400' : 'text-red-600 dark:text-red-400'}`} />
) : (
<TrendingDown className="h-4 w-4 text-red-600 dark:text-red-400" />
)}
</div>
<div>
<p className={`text-lg font-bold ${isPositive ? 'text-emerald-600 dark:text-emerald-400' : 'text-red-600 dark:text-red-400'}`}>
{isPositive ? '+' : ''}{value}%
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">{label}</p>
</div>
</div>
);
}SuperAdminPlansPage function · typescript · L27-L138 (112 LOC)src/app/(super-admin)/super-admin/plans/page.tsx
export default function SuperAdminPlansPage() {
const [plans, setPlans] = useState<Plan[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchPlans();
}, []);
const fetchPlans = async () => {
try {
const res = await fetch('/api/super-admin/plans');
const data = await res.json();
if (data.success) {
setPlans(data.data);
}
} catch (error) {
console.error('Error fetching plans:', error);
} finally {
setLoading(false);
}
};
if (loading) {
return (
<div className="flex items-center justify-center py-12">
<div className="h-8 w-8 animate-spin rounded-full border-4 border-indigo-600 border-t-transparent" />
</div>
);
}
return (
<div>
<div className="mb-8">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Plans</h1>
<p className="mt-1 text-gray-500 dark:text-gray-400">
Manage subscription plans
</p>
SuperAdminUsersPage function · typescript · L19-L125 (107 LOC)src/app/(super-admin)/super-admin/users/page.tsx
export default function SuperAdminUsersPage() {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [search, setSearch] = useState('');
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = async () => {
try {
const res = await fetch('/api/super-admin/users');
const data = await res.json();
if (data.success) {
setUsers(data.data);
}
} catch (error) {
console.error('Error fetching users:', error);
} finally {
setLoading(false);
}
};
const filteredUsers = users.filter(user =>
user.name?.toLowerCase().includes(search.toLowerCase()) ||
user.email?.toLowerCase().includes(search.toLowerCase())
);
if (loading) {
return (
<div className="flex items-center justify-center py-12">
<div className="h-8 w-8 animate-spin rounded-full border-4 border-indigo-600 border-t-transparent" />
</div>
);
}
return (
<div>
generateMetadata function · typescript · L11-L17 (7 LOC)src/app/tags/[slug]/page.tsx
export async function generateMetadata({ params }: TagPageProps): Promise<Metadata> {
const { slug } = await params;
return {
title: `#${slug}`,
description: `Posts tagged with #${slug}`,
};
}TagPage function · typescript · L19-L42 (24 LOC)src/app/tags/[slug]/page.tsx
export default async function TagPage({ params }: TagPageProps) {
const { slug } = await params;
const posts = await getPostsByTag(slug);
return (
<div className="container mx-auto px-4 py-12">
{/* Header */}
<div className="mb-12 text-center">
<span className="mb-4 inline-flex rounded-full bg-gray-100 px-4 py-2 text-lg font-medium text-gray-700 dark:bg-gray-800 dark:text-gray-300">
#{slug}
</span>
<h1 className="mb-4 text-4xl font-bold text-gray-900 dark:text-white">
Posts tagged with #{slug}
</h1>
<p className="text-lg text-gray-600 dark:text-gray-400">
{posts.length} {posts.length === 1 ? 'post' : 'posts'} found
</p>
</div>
{/* Posts Grid */}
<PostList posts={posts} />
</div>
);
}AnalyticsPage function · typescript · L53-L374 (322 LOC)src/app/t/[tenant]/admin/analytics/page.tsx
export default function AnalyticsPage() {
const [loading, setLoading] = useState(true);
const [locked, setLocked] = useState(false);
const [data, setData] = useState<AnalyticsData | null>(null);
const [error, setError] = useState('');
useEffect(() => {
fetchAnalytics();
}, []);
const fetchAnalytics = async () => {
try {
const res = await fetch('/api/admin/analytics');
const result = await res.json();
if (result.locked) {
setLocked(true);
} else if (result.success) {
setData(result.data);
} else {
setError(result.error || 'Failed to load analytics');
}
} catch (err) {
setError('Failed to load analytics');
} finally {
setLoading(false);
}
};
if (loading) {
return (
<div className="flex items-center justify-center py-12">
<div className="h-8 w-8 animate-spin rounded-full border-4 border-indigo-600 border-t-transparent" />
</div>
);
}
// Locked All rows scored by the Repobility analyzer (https://repobility.com)
TenantAdminCategoriesPage function · typescript · L32-L400 (369 LOC)src/app/t/[tenant]/admin/categories/page.tsx
export default function TenantAdminCategoriesPage() {
const { status } = useSession();
const [categories, setCategories] = useState<Category[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [success, setSuccess] = useState('');
// Modal state
const [showModal, setShowModal] = useState(false);
const [editingCategory, setEditingCategory] = useState<Category | null>(null);
const [formData, setFormData] = useState({ name: '', description: '', color: 'bg-blue-500' });
const [saving, setSaving] = useState(false);
// Delete confirmation
const [deleteConfirm, setDeleteConfirm] = useState<string | null>(null);
const [deleting, setDeleting] = useState(false);
useEffect(() => {
if (status === 'authenticated') {
fetchCategories();
}
}, [status]);
const fetchCategories = async () => {
try {
const res = await fetch('/api/admin/categories');
if (!res.ok) throw new Error('Failed to fegetCookie function · typescript · L28-L34 (7 LOC)src/app/t/[tenant]/admin/layout.tsx
function getCookie(name: string): string | null {
if (typeof document === 'undefined') return null;
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop()?.split(';').shift() || null;
return null;
}TenantAdminLayout function · typescript · L63-L381 (319 LOC)src/app/t/[tenant]/admin/layout.tsx
export default function TenantAdminLayout({
children,
}: {
children: React.ReactNode;
}) {
const router = useRouter();
const pathname = usePathname();
const params = useParams();
const { data: session, status } = useSession();
const [sidebarOpen, setSidebarOpen] = useState(false);
const [tenant, setTenant] = useState<TenantInfo | null>(null);
const [accessDenied, setAccessDenied] = useState(false);
const [loading, setLoading] = useState(true);
const [isImpersonating, setIsImpersonating] = useState(false);
const [impersonationUser, setImpersonationUser] = useState<{ name: string; email: string } | null>(null);
const [userRole, setUserRole] = useState<string | undefined>(undefined);
// Cache tenant fetch to avoid repeated API calls on navigation
const tenantFetchedRef = useRef<boolean>(false);
const tenantCacheRef = useRef<TenantInfo | null>(null);
const tenantSlug = params.tenant as string;
const basePath = `/t/${tenantSlug}/admin`;
const isLoginPTenantAdminLoginPage function · typescript · L9-L301 (293 LOC)src/app/t/[tenant]/admin/login/page.tsx
export default function TenantAdminLoginPage() {
const params = useParams();
const router = useRouter();
const tenantSlug = params.tenant as string;
const [isFormLoading, setIsFormLoading] = useState(false);
const [isGoogleLoading, setIsGoogleLoading] = useState(false);
const [error, setError] = useState('');
const [loginMode, setLoginMode] = useState<'owner' | 'author'>('owner');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleGoogleSignIn = async () => {
setIsGoogleLoading(true);
setError('');
try {
const result = await signIn('google', {
callbackUrl: `/t/${tenantSlug}/admin`,
redirect: true,
});
} catch (err) {
setError('An error occurred. Please try again.');
setIsGoogleLoading(false);
}
};
const handleOwnerLogin = async (e: React.FormEvent) => {
e.preventDefault();
setIsFormLoading(true);
setError('');
try {
const result =AdminPagesPage function · typescript · L149-L487 (339 LOC)src/app/t/[tenant]/admin/pages/page.tsx
export default function AdminPagesPage() {
const params = useParams();
const { data: session, status } = useSession();
const tenantSlug = params.tenant as string;
const basePath = `/t/${tenantSlug}/admin`;
const [pages, setPages] = useState<Page[]>([]);
const [loading, setLoading] = useState(true);
const [showTemplates, setShowTemplates] = useState(false);
const [creating, setCreating] = useState(false);
const [error, setError] = useState('');
const [accessDenied, setAccessDenied] = useState(false);
const userRole = (session?.user as any)?.role;
useEffect(() => {
if (status === 'authenticated' && userRole === 'author') {
setAccessDenied(true);
setLoading(false);
return;
}
if (status === 'authenticated') {
fetchPages();
}
}, [status, userRole]);
const fetchPages = async () => {
try {
const response = await fetch('/api/admin/pages');
const data = await response.json();
if (data.success) {
TenantAdminDashboard function · typescript · L55-L411 (357 LOC)src/app/t/[tenant]/admin/page.tsx
export default function TenantAdminDashboard() {
const params = useParams();
const tenantSlug = params.tenant as string;
const basePath = `/t/${tenantSlug}/admin`;
const [stats, setStats] = useState<DashboardStats | null>(null);
const [loading, setLoading] = useState(true);
const [pendingRequest, setPendingRequest] = useState<PendingRequest | null>(null);
const [platformContact, setPlatformContact] = useState<PlatformContact | null>(null);
const [profileComplete, setProfileComplete] = useState(true);
const [showProfileAlert, setShowProfileAlert] = useState(true);
useEffect(() => {
fetchStats();
fetchPlanRequest();
fetchProfileStatus();
}, []);
const fetchStats = async () => {
try {
const res = await fetch('/api/admin/dashboard');
const data = await res.json();
if (data.success) {
setStats(data.data);
}
} catch (error) {
console.error('Error fetching stats:', error);
} finally {
setLoading(falTenantAdminEditPostPage function · typescript · L26-L145 (120 LOC)src/app/t/[tenant]/admin/posts/[id]/edit/page.tsx
export default function TenantAdminEditPostPage() {
const params = useParams();
const router = useRouter();
const { data: session, status } = useSession();
const tenantSlug = params.tenant as string;
const postId = params.id as string;
const basePath = `/t/${tenantSlug}/admin`;
const [authors, setAuthors] = useState<Author[]>([]);
const [post, setPost] = useState<PostData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const userRole = (session?.user as any)?.role;
const userAuthorId = (session?.user as any)?.authorId;
const userName = session?.user?.name;
const isAuthor = userRole === 'author';
useEffect(() => {
if (status === 'loading') return;
const loadData = async () => {
await fetchPost();
// For authors, use their own info - don't fetch authors list
if (isAuthor && userAuthorId) {
setAuthors([{ id: userAuthorId, name: userName || 'You' }]);
} else {
TenantAdminNewPostPage function · typescript · L13-L81 (69 LOC)src/app/t/[tenant]/admin/posts/new/page.tsx
export default function TenantAdminNewPostPage() {
const params = useParams();
const { data: session, status } = useSession();
const tenantSlug = params.tenant as string;
const [authors, setAuthors] = useState<Author[]>([]);
const [loading, setLoading] = useState(true);
const userRole = (session?.user as any)?.role;
const userAuthorId = (session?.user as any)?.authorId;
const userName = session?.user?.name;
const isAuthor = userRole === 'author';
useEffect(() => {
if (status === 'loading') return;
// For authors, use their own info - don't fetch authors list
if (isAuthor && userAuthorId) {
setAuthors([{ id: userAuthorId, name: userName || 'You' }]);
setLoading(false);
return;
}
// For admins/owners, fetch the authors list
fetchAuthors();
}, [status, isAuthor, userAuthorId, userName]);
const fetchAuthors = async () => {
try {
const res = await fetch('/api/admin/authors');
const data = await res.json(Repobility · open methodology · https://repobility.com/research/
ReviewsPage function · typescript · L26-L299 (274 LOC)src/app/t/[tenant]/admin/reviews/page.tsx
export default function ReviewsPage() {
const params = useParams();
const router = useRouter();
const tenant = params.tenant as string;
const [reviews, setReviews] = useState<Review[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [requiresUpgrade, setRequiresUpgrade] = useState(false);
useEffect(() => {
fetchReviews();
}, []);
const fetchReviews = async () => {
try {
const response = await fetch('/api/admin/reviews');
const data = await response.json();
if (data.requiresUpgrade) {
setRequiresUpgrade(true);
setError(data.error);
return;
}
if (data.success) {
setReviews(data.data);
} else {
setError(data.error);
}
} catch (err: any) {
setError(err.message);
} finally {
setLoading(false);
}
};
const handleDelete = async (id: string) => {
if (!confirm('Are you sure you want togenerateMetadata function · typescript · L23-L40 (18 LOC)src/app/t/[tenant]/blog/page.tsx
export async function generateMetadata({ params }: { params: Promise<{ tenant: string }> }): Promise<Metadata> {
const { tenant: tenantSlug } = await params;
const tenant = await getTenantBySlug(tenantSlug);
if (!tenant) return {};
await connectDB();
const settings = await Settings.findOne({ tenantId: tenant._id }).lean();
const siteName = settings?.siteName || tenant.settings.siteName || tenant.name;
const seoDescription = settings?.seoDescription || settings?.siteDescription || 'Read our latest blog posts';
return {
title: {
absolute: `Blog | ${siteName}`,
},
description: seoDescription,
};
}getReviewBySlugOrId function · typescript · L22-L42 (21 LOC)src/app/t/[tenant]/blog/[slug]/page.tsx
async function getReviewBySlugOrId(slugOrId: string, tenantId: string) {
await connectDB();
// First try by slug
let review = await Review.findOne({
tenantId,
'generatedReview.slug': slugOrId,
published: true,
}).lean();
// If not found and looks like MongoDB ObjectId, try by _id
if (!review && /^[a-f0-9]{24}$/i.test(slugOrId)) {
review = await Review.findOne({
_id: slugOrId,
tenantId,
published: true,
}).lean();
}
return review ? JSON.parse(JSON.stringify(review)) : null;
}