Projektstart

This commit is contained in:
2026-01-22 16:19:07 +01:00
parent 5174b88af9
commit bc7fbf8ce6
1553 changed files with 111281 additions and 141 deletions

View File

@@ -14,6 +14,8 @@ type Account = {
provider: string;
oauthConnected?: boolean;
oauthExpiresAt?: string | null;
oauthHealthy?: boolean;
oauthError?: { code: string; message: string };
};
type Rule = {
@@ -89,10 +91,16 @@ export default function App() {
const enriched = await Promise.all((accountsData.accounts ?? []).map(async (account: Account) => {
if (account.provider === "GMAIL") {
try {
const status = await apiFetch(`/oauth/gmail/status/${account.id}`, {}, authToken);
return { ...account, oauthConnected: status.connected, oauthExpiresAt: status.expiresAt };
const status = await apiFetch(`/oauth/gmail/ping/${account.id}`, {}, authToken);
return {
...account,
oauthConnected: status.connected,
oauthHealthy: status.healthy,
oauthExpiresAt: status.expiresAt,
oauthError: status.error
};
} catch {
return { ...account, oauthConnected: false };
return { ...account, oauthConnected: false, oauthHealthy: false };
}
}
return account;
@@ -348,7 +356,7 @@ export default function App() {
<div>
<p className="badge">v0.1</p>
<h1>{t("appName")}</h1>
<p className="tagline">{tenant?.name ?? "Tenant"}</p>
<p className="tagline">{tenant?.name ?? t("tenantFallback")}</p>
</div>
<div className="lang">
<span>{user?.email ?? ""}</span>
@@ -363,7 +371,7 @@ export default function App() {
{lang.label}
</button>
))}
<button type="button" onClick={handleLogout}>Logout</button>
<button type="button" onClick={handleLogout}>{t("logout")}</button>
</div>
</div>
</header>
@@ -562,7 +570,14 @@ export default function App() {
{accounts.map((account) => (
<div key={account.id} className="list-item">
<div>
<strong>{account.email}</strong>
<div className="badge">
<strong>{account.email}</strong>
{account.provider === "GMAIL" && (
<span className={`status-badge ${account.oauthConnected ? "" : "missing"}`}>
{account.oauthConnected ? t("badgeConnected") : t("badgeMissing")}
</span>
)}
</div>
<p>
{account.provider === "GMAIL"
? t("providerGmail")
@@ -575,9 +590,19 @@ export default function App() {
</p>
{account.provider === "GMAIL" && (
<p className="status-note">
{t("statusLabel")}: {account.oauthHealthy ? t("badgeHealthy") : t("badgeUnhealthy")} ·{" "}
{t("adminExpiresAt")}: {account.oauthExpiresAt ? new Date(account.oauthExpiresAt).toLocaleString() : t("oauthStatusUnknown")}
</p>
)}
{account.provider === "GMAIL" && account.oauthError && (
<p className="status-note">
{account.oauthError.code === "invalid_grant"
? t("oauthErrorInvalidGrant")
: account.oauthError.code === "token_expired"
? t("oauthErrorExpired")
: t("oauthErrorUnknown")}
</p>
)}
</div>
{account.provider === "GMAIL" && (
<button className="ghost" onClick={() => startGmailOauth(account.id)}>
@@ -596,7 +621,10 @@ export default function App() {
<div key={rule.id} className="list-item">
<div>
<strong>{rule.name}</strong>
<p>{rule.conditions.length} Bedingungen · {rule.actions.length} Aktionen</p>
<p>
{t("ruleConditionsCount", { count: rule.conditions.length })} ·{" "}
{t("ruleActionsCount", { count: rule.actions.length })}
</p>
</div>
<button className="ghost" onClick={() => handleDeleteRule(rule.id)}>{t("delete")}</button>
</div>