Merge pull request 'fix(ui): use app icon in header' (#3) from fix/use-real-app-icon into main
Reviewed-on: #3
This commit was merged in pull request #3.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
|
||||
## UI Related
|
||||
- [ ] Fix Theming to be more professional (use frontend themeing skill & make it look like a professional product)
|
||||
|
||||
- [x] replace emoji in the top left with the actual icon
|
||||
|
||||
## Future BS
|
||||
- [ ] CPU, MEM, NET, DISK sentinal first-installed on EC2s to expose a backend for the user's ui to hit, THROUGH A BACKEND PROXY ROUTE so we can cache and rate limit the requests. This will allow for a better dashboard and better stats for the user to see. We'll use an obscure port for the sentinal so that we dont hit any other services the user may have. Sentinal is our own little C program exposing the stats we need, seperate repo for that thing tho!
|
||||
|
||||
@@ -18,7 +18,7 @@ import { runWebhookReconciliation } from "./services/webhookReconcile";
|
||||
|
||||
import { loginHandler, logoutHandler, meHandler, setupStatusHandler, firstUserHandler } from "./routes/auth";
|
||||
import { webhookHandler } from "./routes/webhook";
|
||||
import { getUserSettings, updateUsername, updatePassword, updateGitea, updateAws, getWebhookSecret, regenerateWebhookSecret } from "./routes/api/user";
|
||||
import { getUserSettings, updateUsername, updatePassword, updateGitea, updateAws, skipSetupWizard, getWebhookSecret, regenerateWebhookSecret } from "./routes/api/user";
|
||||
import { listRepos, saveRepoConfig, toggleRepoEnabled, getRepoConfig } from "./routes/api/repos";
|
||||
import { listPreviews, getPreview, stopPreviewRoute, rebuildPreviewRoute, previewLogsWs, previewAppLogsWs } from "./routes/api/previews";
|
||||
import { getStats } from "./routes/api/stats";
|
||||
@@ -66,6 +66,7 @@ server.path("/", (path) => path
|
||||
.http("PATCH", "/api/user/password", (http) => http.onRequest(updatePassword))
|
||||
.http("PUT", "/api/user/gitea", (http) => http.onRequest(updateGitea))
|
||||
.http("PUT", "/api/user/aws", (http) => http.onRequest(updateAws))
|
||||
.http("POST", "/api/user/setup/skip", (http) => http.onRequest(skipSetupWizard))
|
||||
.http("GET", "/api/user/webhook-secret", (http) => http.onRequest(getWebhookSecret))
|
||||
.http("POST", "/api/user/webhook-secret/regenerate", (http) => http.onRequest(regenerateWebhookSecret))
|
||||
);
|
||||
|
||||
@@ -181,6 +181,18 @@ export async function updateAws(ctr: any) {
|
||||
return makeResponse({ ctr, content: { code: 200, message: `Connected as ${validation.arn}`, data: { arn: validation.arn } } });
|
||||
}
|
||||
|
||||
export async function skipSetupWizard(ctr: any) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
|
||||
await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: { setupSkipped: true },
|
||||
});
|
||||
|
||||
return makeResponse({ ctr, content: { code: 200, message: "Setup wizard skipped" } });
|
||||
}
|
||||
|
||||
export async function getWebhookSecret(ctr: any) {
|
||||
const user = requireAuth(ctr);
|
||||
if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } });
|
||||
|
||||
@@ -10,6 +10,17 @@ const log = createLogger("AUTH_ROUTE");
|
||||
const COOKIE_NAME = "pp_session";
|
||||
const COOKIE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;
|
||||
|
||||
function isSetupComplete(user: {
|
||||
setupSkipped: boolean;
|
||||
giteaInstanceUrl: string | null;
|
||||
giteaPAT: string | null;
|
||||
awsAccessKeyId: string | null;
|
||||
awsSecretAccessKey: string | null;
|
||||
awsRegion: string | null;
|
||||
}) {
|
||||
return user.setupSkipped || !!(user.giteaInstanceUrl && user.giteaPAT && user.awsAccessKeyId && user.awsSecretAccessKey && user.awsRegion);
|
||||
}
|
||||
|
||||
export async function loginHandler(ctr: any) {
|
||||
let body: any;
|
||||
try {
|
||||
@@ -82,7 +93,8 @@ export async function meHandler(ctr: any) {
|
||||
awsAccessKeyId: user.awsAccessKeyId ? "****" : null,
|
||||
awsRegion: user.awsRegion,
|
||||
awsConfigured: !!(user.awsAccessKeyId && user.awsSecretAccessKey && user.awsRegion),
|
||||
setupComplete: !!(user.giteaInstanceUrl && user.giteaPAT && user.awsAccessKeyId && user.awsSecretAccessKey && user.awsRegion),
|
||||
setupSkipped: user.setupSkipped,
|
||||
setupComplete: isSetupComplete(user),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
import { useAuth } from "../hooks/useAuth";
|
||||
import { ThemePreference, useTheme } from "../hooks/useTheme";
|
||||
import { api } from "../services/api";
|
||||
import { Rocket, Sun, Moon, Monitor } from "lucide-react";
|
||||
import { Sun, Moon, Monitor } from "lucide-react";
|
||||
|
||||
const themeOptions: { value: ThemePreference; label: string; Icon: typeof Sun }[] = [
|
||||
{ value: "light", label: "Light", Icon: Sun },
|
||||
@@ -42,7 +42,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
||||
<div className="max-w-7xl mx-auto px-4 h-14 flex items-center justify-between gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Link to="/" className="font-bold text-lg text-blue-600 dark:text-blue-400 hover:opacity-80 inline-flex items-center gap-1.5">
|
||||
<Rocket size={20} /> PR Previews
|
||||
<img src="/icon.png" alt="" className="h-5 w-5 shrink-0 rounded" aria-hidden="true" /> PR Previews
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface AuthUser {
|
||||
username: string;
|
||||
isAdmin: boolean;
|
||||
isFounder: boolean;
|
||||
setupSkipped: boolean;
|
||||
setupComplete: boolean;
|
||||
giteaUsername: string | null;
|
||||
giteaInstanceUrl: string | null;
|
||||
|
||||
@@ -44,7 +44,32 @@ export function SetupWizard() {
|
||||
}, [step, user?.id]);
|
||||
|
||||
const next = () => setStep(s => Math.min(s + 1, STEPS.length - 1));
|
||||
const skip = () => navigate("/");
|
||||
const skip = async () => {
|
||||
setSaving(true);
|
||||
const res = await api.user.skipSetup();
|
||||
setSaving(false);
|
||||
if (res.ok) {
|
||||
await refresh();
|
||||
navigate("/");
|
||||
} else {
|
||||
toast.error(res.message || "Failed to skip setup");
|
||||
}
|
||||
};
|
||||
const leaveWizard = async (path: string) => {
|
||||
if (user?.setupComplete) {
|
||||
navigate(path);
|
||||
return;
|
||||
}
|
||||
setSaving(true);
|
||||
const res = await api.user.skipSetup();
|
||||
setSaving(false);
|
||||
if (res.ok) {
|
||||
await refresh();
|
||||
navigate(path);
|
||||
} else {
|
||||
toast.error(res.message || "Failed to finish setup");
|
||||
}
|
||||
};
|
||||
|
||||
const handleGiteaNext = async () => {
|
||||
setSaving(true);
|
||||
@@ -112,8 +137,8 @@ export function SetupWizard() {
|
||||
<button onClick={next} className="inline-flex items-center gap-1 px-6 py-2.5 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-medium transition-colors">
|
||||
Get Started <ArrowRight size={16} />
|
||||
</button>
|
||||
<button onClick={skip} className="px-4 py-2.5 text-sm text-gray-500 dark:text-slate-400 hover:underline">
|
||||
Skip wizard
|
||||
<button onClick={skip} disabled={saving} className="px-4 py-2.5 text-sm text-gray-500 dark:text-slate-400 hover:underline disabled:opacity-50">
|
||||
{saving ? "Skipping..." : "Skip wizard"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -211,10 +236,10 @@ export function SetupWizard() {
|
||||
Go to the Repos page to enable previews for a repository. PP will automatically register the webhook.
|
||||
</p>
|
||||
<div className="flex gap-3">
|
||||
<a href="/repos"
|
||||
className="flex-1 inline-flex items-center justify-center gap-1 py-2.5 text-center bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-medium transition-colors">
|
||||
<button onClick={() => leaveWizard("/repos")} disabled={saving}
|
||||
className="flex-1 inline-flex items-center justify-center gap-1 py-2.5 text-center bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-medium transition-colors disabled:opacity-50">
|
||||
Go to Repos <ArrowRight size={16} />
|
||||
</a>
|
||||
</button>
|
||||
<button onClick={next} className="text-sm text-gray-500 dark:text-slate-400 hover:underline px-2">Skip</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -227,8 +252,8 @@ export function SetupWizard() {
|
||||
<p className="text-gray-600 dark:text-slate-300 text-sm">
|
||||
Open a pull request on an enabled repo and PP will provision a preview automatically.
|
||||
</p>
|
||||
<button onClick={() => navigate("/")} className="inline-flex items-center gap-1 px-6 py-2.5 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-medium transition-colors">
|
||||
Go to Dashboard <ArrowRight size={16} />
|
||||
<button onClick={() => leaveWizard("/")} disabled={saving} className="inline-flex items-center gap-1 px-6 py-2.5 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-medium transition-colors disabled:opacity-50">
|
||||
{saving ? "Finishing..." : <span className="inline-flex items-center gap-1">Go to Dashboard <ArrowRight size={16} /></span>}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -37,6 +37,7 @@ export const api = {
|
||||
request("PUT", "/user/gitea", data),
|
||||
updateAws: (data: { awsAccessKeyId: string; awsSecretAccessKey: string; awsRegion: string }) =>
|
||||
request("PUT", "/user/aws", data),
|
||||
skipSetup: () => request("POST", "/user/setup/skip"),
|
||||
getWebhookSecret: () => request("GET", "/user/webhook-secret"),
|
||||
regenerateWebhookSecret: () => request("POST", "/user/webhook-secret/regenerate"),
|
||||
},
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "User" ADD COLUMN "setupSkipped" BOOLEAN NOT NULL DEFAULT false;
|
||||
@@ -36,6 +36,7 @@ model User {
|
||||
passwordHash String @db.VarChar(256)
|
||||
isAdmin Boolean @default(false)
|
||||
isFounder Boolean @default(false)
|
||||
setupSkipped Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
|
||||
Reference in New Issue
Block a user