first commit

This commit is contained in:
mstfyldz
2026-03-24 15:46:27 +03:00
parent 095d830279
commit 34b6a46604
33 changed files with 5212 additions and 81 deletions

View File

@@ -1,65 +1,227 @@
import Image from "next/image";
import { logout } from "./login/actions";
import { getApps } from "./apps/actions";
import { getAppStatus } from "./asc/actions";
import {
LayoutDashboard,
Smartphone,
Settings,
Bell,
CloudUpload,
Plus,
ExternalLink,
Search,
CheckCircle2,
Clock,
XCircle,
RefreshCw,
LogOut,
SlidersHorizontal
} from "lucide-react";
import Link from "next/link";
const statusMap: Record<string, { label: string, color: string, bg: string }> = {
READY_FOR_SALE: { label: "Yayında", color: "text-emerald-700 dark:text-emerald-400", bg: "bg-emerald-100 dark:bg-emerald-900/30" },
PREPARE_FOR_SUBMISSION: { label: "Hazırlanıyor", color: "text-blue-700 dark:text-blue-400", bg: "bg-blue-100 dark:bg-blue-900/30" },
WAITING_FOR_REVIEW: { label: "İnceleme Bekliyor", color: "text-amber-700 dark:text-amber-400", bg: "bg-amber-100 dark:bg-amber-900/30" },
IN_REVIEW: { label: "İncelemede", color: "text-purple-700 dark:text-purple-400", bg: "bg-purple-100 dark:bg-purple-900/30" },
REJECTED: { label: "Reddedildi", color: "text-red-700 dark:text-red-400", bg: "bg-red-100 dark:bg-red-900/30" },
METADATA_REJECTED: { label: "Metadata Reddi", color: "text-red-700 dark:text-red-400", bg: "bg-red-100 dark:bg-red-900/30" },
PENDING_DEVELOPER_RELEASE: { label: "Onay Bekliyor", color: "text-cyan-700 dark:text-cyan-400", bg: "bg-cyan-100 dark:bg-cyan-900/30" },
PROCESSING_FOR_APP_STORE: { label: "İşleniyor", color: "text-zinc-600 dark:text-zinc-400", bg: "bg-zinc-100 dark:bg-zinc-800" },
};
export default async function Home() {
const allApps = await getApps();
const appsWithStatus = await Promise.all(allApps.map(async (app) => {
if (app.platform === 'ios' && app.appleId) {
try {
const { data } = await getAppStatus(app.appleId);
const latestVersion = data?.data?.[0];
const state = latestVersion?.attributes?.appStoreState;
const versionNumber = latestVersion?.attributes?.versionString;
return { ...app, apiStatus: state, version: versionNumber };
} catch (err) {
return { ...app, apiStatus: 'ERROR' };
}
}
return { ...app, apiStatus: 'UNKNOWN' };
}));
const inReviewCount = appsWithStatus.filter(a => a.apiStatus === 'IN_REVIEW' || a.apiStatus === 'WAITING_FOR_REVIEW').length;
const liveCount = appsWithStatus.filter(a => a.apiStatus === 'READY_FOR_SALE').length;
const rejectedCount = appsWithStatus.filter(a => a.apiStatus === 'REJECTED' || a.apiStatus === 'METADATA_REJECTED').length;
export default function Home() {
return (
<div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
<main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={100}
height={20}
priority
/>
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
To get started, edit the page.tsx file.
</h1>
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
Looking for a starting point or more instructions? Head over to{" "}
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Templates
</a>{" "}
or the{" "}
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Learning
</a>{" "}
center.
</p>
<div className="flex h-screen bg-slate-50 dark:bg-black font-sans text-slate-900 dark:text-slate-100">
{/* Sidebar */}
<aside className="w-64 bg-white dark:bg-zinc-950 border-r border-slate-200 dark:border-zinc-900 flex flex-col shrink-0 shadow-sm">
<div className="p-6 border-b border-slate-100 dark:border-zinc-900 flex items-center gap-3">
<div className="w-7 h-7 bg-slate-900 dark:bg-white rounded-md flex items-center justify-center">
<span className="text-white dark:text-black font-bold text-sm">A</span>
</div>
<span className="font-bold text-lg tracking-tight uppercase tracking-wider">AppAdmin</span>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<a
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={16}
height={16}
/>
Deploy Now
</a>
<a
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</a>
<nav className="flex-1 p-4 space-y-1">
<Link href="/" className="flex items-center gap-2.5 px-3 py-2 bg-slate-900 dark:bg-white text-white dark:text-black rounded-lg text-sm font-semibold transition-all">
<LayoutDashboard size={18} /> Dashboard
</Link>
<Link href="/apps" className="flex items-center gap-2.5 px-3 py-2 text-slate-600 dark:text-zinc-400 hover:bg-slate-50 dark:hover:bg-zinc-900 hover:text-slate-900 dark:hover:text-white rounded-lg text-sm font-medium">
<Smartphone size={18} /> Uygulamalar
</Link>
<Link href="/notifications" className="flex items-center gap-2.5 px-3 py-2 text-slate-600 dark:text-zinc-400 hover:bg-slate-50 dark:hover:bg-zinc-900 hover:text-slate-900 dark:hover:text-white rounded-lg text-sm font-medium">
<Bell size={18} /> Bildirimler
</Link>
</nav>
<div className="p-4 border-t border-slate-100 dark:border-zinc-900">
<form action={logout}>
<button className="flex items-center gap-2.5 px-3 py-2 w-full text-slate-500 dark:text-zinc-400 hover:text-red-600 dark:hover:text-red-400 transition-colors text-sm font-medium">
<LogOut size={18} /> Çıkış Yap
</button>
</form>
</div>
</aside>
{/* Main Content */}
<main className="flex-1 overflow-y-auto">
<header className="h-16 bg-white/80 dark:bg-zinc-950/80 backdrop-blur-md border-b border-slate-200 dark:border-zinc-900 flex items-center justify-between px-8 sticky top-0 z-10">
<div className="flex items-center gap-3">
<h2 className="text-md font-bold text-slate-900 dark:text-white uppercase tracking-wider">Genel Bakış</h2>
<span className="px-2 py-0.5 bg-emerald-50 dark:bg-emerald-900/30 text-emerald-600 dark:text-emerald-400 text-[10px] font-bold rounded-full border border-emerald-100 dark:border-emerald-800 uppercase tracking-widest">
Live
</span>
</div>
<div className="flex items-center gap-4">
<div className="relative group">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 group-focus-within:text-slate-900 transition-colors" size={16} />
<input className="pl-9 pr-4 py-1.5 bg-slate-50 dark:bg-zinc-900 border border-slate-200 dark:border-zinc-800 rounded-lg text-sm w-64 text-slate-900 dark:text-white focus:outline-none focus:ring-1 focus:ring-slate-400 dark:focus:ring-zinc-700 transition-all" placeholder="Uygulama ara..." />
</div>
</div>
</header>
<div className="p-8 space-y-8">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<StatCard title="Uygulamalar" value={allApps.length.toString()} icon={<Smartphone size={20}/>} color="bg-blue-600" />
<StatCard title="İnceleme" value={inReviewCount.toString()} icon={<Clock size={20}/>} color="bg-amber-500" />
<StatCard title="Reddedilen" value={rejectedCount.toString()} icon={<XCircle size={20}/>} color="bg-red-500" />
<StatCard title="Yayında" value={liveCount.toString()} icon={<CheckCircle2 size={20}/>} color="bg-emerald-500" />
</div>
<div className="grid grid-cols-1 xl:grid-cols-3 gap-8">
<div className="xl:col-span-2 space-y-6">
<div className="flex items-center justify-between border-b border-slate-200 dark:border-zinc-900 pb-4">
<h3 className="text-lg font-bold text-slate-900 dark:text-white uppercase tracking-wider">Uygulamalar</h3>
<Link href="/apps" className="flex items-center gap-1.5 px-4 py-1.5 bg-slate-900 dark:bg-white text-white dark:text-black rounded-lg text-xs font-bold hover:opacity-90 transition-all">
<Plus size={14} /> Yeni Ekle
</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{appsWithStatus.map((app) => {
const statusInfo = statusMap[app.apiStatus || ''] || { label: "Bilinmiyor", color: "text-slate-500 dark:text-zinc-400", bg: "bg-slate-100 dark:bg-zinc-900" };
return (
<div key={app.id} className="bg-white dark:bg-zinc-950 p-6 rounded-xl border border-slate-200 dark:border-zinc-900 hover:shadow-sm hover:border-slate-300 dark:hover:border-zinc-800 transition-all group">
<div className="flex justify-between items-start mb-6">
<div className="w-11 h-11 bg-slate-50 dark:bg-zinc-900 rounded-lg flex items-center justify-center border border-slate-100 dark:border-zinc-800 overflow-hidden">
{app.platform === 'ios' ? <Image src="/next.svg" alt="iOS" width={22} height={22} className="dark:invert p-0.5" /> : <Smartphone size={22} className="text-slate-400" />}
</div>
<div className={`px-2.5 py-0.5 rounded-md text-[10px] font-bold uppercase tracking-widest border border-slate-100 dark:border-zinc-800 ${statusInfo.bg} ${statusInfo.color}`}>
{statusInfo.label}
</div>
</div>
<div className="space-y-0.5">
<div className="flex items-baseline gap-2">
<h4 className="font-bold text-md text-slate-900 dark:text-white truncate max-w-[140px]">{app.name}</h4>
{(app as any).version && <span className="text-[10px] font-mono text-slate-400">v{(app as any).version}</span>}
</div>
<p className="text-[10px] text-slate-400 font-mono truncate">{app.bundleId}</p>
</div>
<div className="mt-6 flex items-center justify-between pt-4 border-t border-slate-50 dark:border-zinc-900/50">
<div className="flex gap-4">
{/* Remote Config Butonu (Yeni) */}
<Link href={`/apps/${app.id}/config`} className="flex items-center gap-1.5 px-2 py-1 bg-slate-50 dark:bg-zinc-900 border border-slate-200 dark:border-zinc-800 rounded text-[10px] font-bold uppercase tracking-wider text-slate-600 dark:text-slate-400 hover:text-blue-600 transition-all">
<SlidersHorizontal size={12} /> Config
</Link>
{/* Edit Butonu */}
<Link href={`/apps/${app.id}/edit`} className="flex items-center gap-1.5 px-2 py-1 bg-slate-50 dark:bg-zinc-900 border border-slate-200 dark:border-zinc-800 rounded text-[10px] font-bold uppercase tracking-wider text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white transition-all">
<Settings size={12} /> Edit
</Link>
</div>
<a href={`https://apps.apple.com/app/id${app.appleId}`} target="_blank" className="text-slate-400 hover:text-blue-600 transition-colors">
<ExternalLink size={18} />
</a>
</div>
</div>
);
})}
</div>
</div>
<div className="xl:col-span-1 space-y-8">
<div className="bg-white dark:bg-zinc-950 p-6 rounded-xl border border-slate-200 dark:border-zinc-900">
<h3 className="text-sm font-bold mb-5 text-slate-400 uppercase tracking-widest">Sistem Durumu</h3>
<div className="space-y-4">
<div className="flex items-center justify-between">
<span className="text-xs text-slate-600 dark:text-zinc-400 font-semibold uppercase tracking-wider">Apple Connect API</span>
<span className="flex items-center gap-1.5 text-emerald-600 text-[10px] font-bold uppercase tracking-widest">
<div className="w-1.5 h-1.5 bg-emerald-500 rounded-full" /> Bağlı
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-xs text-slate-600 dark:text-zinc-400 font-semibold uppercase tracking-wider">PostgreSQL DB</span>
<span className="flex items-center gap-1.5 text-emerald-600 text-[10px] font-bold uppercase tracking-widest">
<div className="w-1.5 h-1.5 bg-emerald-500 rounded-full" /> Aktif
</span>
</div>
</div>
</div>
<div className="bg-white dark:bg-zinc-950 p-6 rounded-xl border border-slate-200 dark:border-zinc-900">
<h3 className="text-sm font-bold mb-5 flex items-center justify-between text-slate-400 uppercase tracking-widest">
Hızlı İşlemler
<RefreshCw size={14} className="hover:rotate-180 transition-all duration-500 cursor-pointer" />
</h3>
<div className="space-y-2">
<QuickActionButton label="Duyuru Yayınla" icon={<Bell size={16}/>} />
<QuickActionButton label="Yenile" icon={<CloudUpload size={16}/>} />
</div>
</div>
</div>
</div>
</div>
</main>
</div>
);
}
function StatCard({ title, value, icon, color }: { title: string, value: string, icon: any, color: string }) {
return (
<div className="bg-white dark:bg-zinc-950 p-6 rounded-xl border border-slate-200 dark:border-zinc-900">
<div className="flex items-center justify-between mb-3">
<span className="text-[10px] text-slate-400 font-bold uppercase tracking-[0.1em]">{title}</span>
<div className={`p-1.5 rounded-md text-white ${color} shadow-sm`}>
{icon}
</div>
</div>
<div className="text-3xl font-bold text-slate-900 dark:text-white tabular-nums">{value}</div>
</div>
)
}
function QuickActionButton({ label, icon }: { label: string, icon: any }) {
return (
<button className="w-full flex items-center justify-between p-3 rounded-lg border border-slate-50 dark:border-zinc-900 hover:bg-slate-50 dark:hover:bg-zinc-900 hover:border-slate-100 dark:hover:border-zinc-800 transition-all text-xs font-bold text-slate-700 dark:text-zinc-300">
<div className="flex items-center gap-3">
{icon}
{label}
</div>
<Plus size={14} className="text-slate-300" />
</button>
)
}