first commit

This commit is contained in:
AyrisAI
2026-05-14 01:57:52 +03:00
parent 863a32cd35
commit 4a9196f483
47 changed files with 12043 additions and 102 deletions

View File

@@ -0,0 +1,142 @@
"use client";
import { useState, useEffect } from "react";
interface User {
id: string;
name: string;
email: string;
role: string;
domains: string[];
}
export default function UsersPage() {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [search, setSearch] = useState("");
useEffect(() => {
fetch("/api/users")
.then((r) => r.json())
.then((data) => {
setUsers(Array.isArray(data) ? data : []);
setLoading(false);
})
.catch(() => setLoading(false));
}, []);
const filtered = users.filter(
(u) =>
u.name.toLowerCase().includes(search.toLowerCase()) ||
u.email.toLowerCase().includes(search.toLowerCase())
);
return (
<>
<div className="page-header">
<div>
<h1 className="page-title">Kullanıcılar</h1>
<p className="page-subtitle">Panel kullanıcıları .env dosyasından yönetilir</p>
</div>
</div>
<div className="page-body">
{/* Info card */}
<div className="card" style={{ display: "flex", gap: 14, alignItems: "flex-start", border: "1px solid var(--accent-dim)", background: "var(--accent-dim)" }}>
<div style={{ color: "var(--accent-hover)", flexShrink: 0, paddingTop: 2 }}>
<InfoIcon />
</div>
<div>
<div style={{ fontWeight: 600, color: "var(--accent-hover)", marginBottom: 6 }}>Kullanıcı yönetimi hakkında</div>
<div style={{ fontSize: 13, color: "var(--text-secondary)", lineHeight: 1.7 }}>
Kullanıcılar <code style={{ background: "var(--bg)", padding: "1px 6px", borderRadius: 4, fontSize: 12 }}>.env</code> dosyasındaki{" "}
<code style={{ background: "var(--bg)", padding: "1px 6px", borderRadius: 4, fontSize: 12 }}>USER_0_*</code>,{" "}
<code style={{ background: "var(--bg)", padding: "1px 6px", borderRadius: 4, fontSize: 12 }}>USER_1_*</code> değişkenleriyle tanımlanır.
Yeni kullanıcı eklemek için .env dosyasını düzenleyip uygulamayı yeniden başlatın.
</div>
<div style={{ marginTop: 12, padding: "10px 14px", background: "var(--bg)", borderRadius: "var(--radius)", fontSize: 12, fontFamily: "monospace", color: "var(--text-secondary)", lineHeight: 2 }}>
USER_2_NAME=&quot;Ahmet Yılmaz&quot;<br />
USER_2_EMAIL=&quot;ahmet@ayristech.com&quot;<br />
USER_2_PASSWORD=&quot;güçlü-şifre&quot;<br />
USER_2_ROLE=&quot;DOMAIN_ADMIN&quot;<br />
USER_2_DOMAINS=&quot;yenidomain.com&quot;
</div>
</div>
</div>
<div className="search-bar">
<div className="search-input-wrap">
<span className="search-icon"><SearchIcon /></span>
<input
type="text"
className="input search-input"
placeholder="İsim veya e-posta ara..."
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</div>
</div>
<div className="table-wrap">
{loading ? (
<div className="empty-state">
<span className="spinner" style={{ width: 24, height: 24 }} />
</div>
) : filtered.length === 0 ? (
<div className="empty-state">
<div className="empty-icon"><UsersIcon /></div>
<div style={{ fontWeight: 600 }}>Kullanıcı bulunamadı</div>
</div>
) : (
<table>
<thead>
<tr>
<th>Kullanıcı</th>
<th>Rol</th>
<th>İzin Verilen Domainler</th>
</tr>
</thead>
<tbody>
{filtered.map((u) => (
<tr key={u.id}>
<td>
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
<div className="user-avatar" style={{ width: 32, height: 32, fontSize: 13 }}>
{u.name[0]?.toUpperCase()}
</div>
<div>
<div style={{ fontWeight: 500 }}>{u.name}</div>
<div style={{ fontSize: 11, color: "var(--text-secondary)" }}>{u.email}</div>
</div>
</div>
</td>
<td>
<span className={`badge ${u.role === "SUPER_ADMIN" ? "badge-blue" : "badge-green"}`}>
{u.role === "SUPER_ADMIN" ? "★ Süper Admin" : "Domain Admin"}
</span>
</td>
<td>
{u.domains.includes("*") ? (
<span className="badge badge-blue">Tüm domainler</span>
) : (
<div style={{ display: "flex", flexWrap: "wrap", gap: 4 }}>
{u.domains.map((d) => (
<span key={d} className="badge badge-green">{d}</span>
))}
</div>
)}
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</div>
</>
);
}
function SearchIcon() { return <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>; }
function UsersIcon() { return <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>; }
function InfoIcon() { return <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>; }