diff --git a/frontend/public/logo.svg b/frontend/public/logo.svg
new file mode 100644
index 0000000..5e4619f
--- /dev/null
+++ b/frontend/public/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/src/components/Logo.tsx b/frontend/src/components/Logo.tsx
index 1ff3e79..17bdf03 100644
--- a/frontend/src/components/Logo.tsx
+++ b/frontend/src/components/Logo.tsx
@@ -2,63 +2,63 @@ import { DotIcon } from "@phosphor-icons/react";
import "@fontsource/knewave/400.css";
interface LogoProps {
- scale?: number;
- type?: "inline" | "mono" | "logo" | null;
- ul?: boolean;
+ scale?: number;
+ type?: "inline" | "mono" | "logo" | null;
+ ul?: boolean;
}
export default function Logo({
- scale = 1,
- type = null,
- ul = false,
+ scale = 1,
+ type = null,
+ ul = false,
}: LogoProps) {
- if (type === "inline") {
- return (
-
- pi. ku
- .
-
- );
- }
+ if (type === "inline") {
+ return (
+
+ pi. ku
+ .
+
+ );
+ }
- if (type === "mono") {
- return (
-
- pi. ku.
-
- );
- }
+ if (type === "mono") {
+ return (
+
+ pi. ku.
+
+ );
+ }
- if (type === "logo") {
- return (
-
- );
- }
+ if (type === "logo") {
+ return (
+
+ );
+ }
- return (
-
- Pi
-
- Ku
-
-
- );
+ return (
+
+ Pi
+
+ Ku
+
+
+ );
}
diff --git a/frontend/src/index.css b/frontend/src/index.css
index ae1cb96..b2cec31 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -24,9 +24,9 @@
--color-neutral: oklch(38% 0.02 45);
--color-neutral-content: oklch(80% 0.015 60);
- --color-info: oklch(60% 0.07 240);
+ --color-info: oklch(60% 0.06 250);
--color-info-content: oklch(95% 0.01 240);
- --color-success: oklch(60% 0.08 150);
+ --color-success: oklch(65% 0.05 140);
--color-success-content: oklch(16% 0.03 150);
--color-warning: oklch(68% 0.08 72);
--color-warning-content: oklch(18% 0.03 60);
@@ -64,7 +64,7 @@
}
.glass-card {
- @apply bg-glass-bg backdrop-blur-xl border border-neutral-content/10 shadow-warm rounded-xl m-4;
+ @apply bg-glass-bg max-w-xs md:max-w-sm backdrop-blur-xl border border-neutral-content/10 shadow-warm rounded-xl m-4;
}
.ul-wavy {
diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx
index 4ed7089..5183c37 100644
--- a/frontend/src/pages/Login.tsx
+++ b/frontend/src/pages/Login.tsx
@@ -16,135 +16,135 @@ import { useAuth } from "../hooks/useAuth";
import { CryptoUtils } from "../utils/crypto";
const loginSchema = z.object({
- email: z.email("Please enter a valid email"),
- password: z.string().min(1, "Password is required"),
+ email: z.email("Please enter a valid email"),
+ password: z.string().min(1, "Password is required"),
});
type LoginInputs = z.infer;
export default function Login() {
- const navigate = useNavigate();
- const location = useLocation();
- const [isLoading, setIsLoading] = useState(false);
- const [apiError, setApiError] = useState(null);
- const { setAuthStore } = useAuth();
- const [showWelcome, setShowWelcome] = useState(!!location.state?.firstTime);
- const [saajanMessage, setSaajanMessage] = useState(
- "I was wondering when you'd return.",
- );
- const nextRoute = location.state?.redirectUrl || ROUTES.DRAWER;
+ const navigate = useNavigate();
+ const location = useLocation();
+ const [isLoading, setIsLoading] = useState(false);
+ const [apiError, setApiError] = useState(null);
+ const { setAuthStore } = useAuth();
+ const [showWelcome, setShowWelcome] = useState(!!location.state?.firstTime);
+ const [saajanMessage, setSaajanMessage] = useState(
+ "I was wondering when you'd return.",
+ );
+ const nextRoute = location.state?.redirectUrl || ROUTES.DRAWER;
- const {
- register,
- handleSubmit,
- formState: { errors },
- } = useForm({
- resolver: zodResolver(loginSchema),
- });
+ const {
+ register,
+ handleSubmit,
+ formState: { errors },
+ } = useForm({
+ resolver: zodResolver(loginSchema),
+ });
- const onSubmit = async (data: LoginInputs) => {
- setIsLoading(true);
- setApiError(null);
- try {
- // client side key derivation for e2e encryption
- const { masterKey, authHash } = await CryptoUtils.deriveKeyBundle(
- data.password,
- data.email,
- );
+ const onSubmit = async (data: LoginInputs) => {
+ setIsLoading(true);
+ setApiError(null);
+ try {
+ // client side key derivation for e2e encryption
+ const { masterKey, authHash } = await CryptoUtils.deriveKeyBundle(
+ data.password,
+ data.email,
+ );
- // send just the authHash as the password to the server
- const { data: authData } = await publicApi.post(endpoints.LOGIN, {
- email: data.email,
- password: authHash,
- });
+ // send just the authHash as the password to the server
+ const { data: authData } = await publicApi.post(endpoints.LOGIN, {
+ email: data.email,
+ password: authHash,
+ });
- const { data: userData } = await api.get(endpoints.ME, {
- headers: { Authorization: `Bearer ${authData.access}` },
- });
+ const { data: userData } = await api.get(endpoints.ME, {
+ headers: { Authorization: `Bearer ${authData.access}` },
+ });
- await setAuthStore(authData.access, userData, masterKey);
+ await setAuthStore(authData.access, userData, masterKey);
- navigate(nextRoute, { replace: true, state: location.state });
- } catch (err) {
- let message =
- "Sorry, we're experiencing technical issues.\nPlease try again later.";
- if (axios.isAxiosError(err) && err.response?.status !== 500) {
- message = err.response?.data?.detail || err.response?.data?.message;
- }
- setApiError(message);
- } finally {
- setIsLoading(false);
- }
- };
-
- return (
-
- {!showWelcome &&
}
- {showWelcome &&
}
-
+
+ );
}
diff --git a/frontend/src/pages/Register.tsx b/frontend/src/pages/Register.tsx
index a7e9fb4..174da9e 100644
--- a/frontend/src/pages/Register.tsx
+++ b/frontend/src/pages/Register.tsx
@@ -14,158 +14,158 @@ import { ROUTES } from "../config/routes";
import { CryptoUtils } from "../utils/crypto";
const registerSchema = z
- .object({
- full_name: z.string().min(2, "Name must be at least 2 characters"),
- email: z.email("Please enter a valid email"),
- password: z.string().min(8, "Password must be at least 8 characters"),
- confirm_password: z.string(),
- })
- .refine((data) => data.password === data.confirm_password, {
- message: "Passwords don't match",
- path: ["confirm_password"],
- });
+ .object({
+ full_name: z.string().min(2, "Name must be at least 2 characters"),
+ email: z.email("Please enter a valid email"),
+ password: z.string().min(8, "Password must be at least 8 characters"),
+ confirm_password: z.string(),
+ })
+ .refine((data) => data.password === data.confirm_password, {
+ message: "Passwords don't match",
+ path: ["confirm_password"],
+ });
type RegisterInputs = z.infer;
export default function Register() {
- const navigate = useNavigate();
- const [isLoading, setIsLoading] = useState(false);
- const [apiError, setApiError] = useState(null);
- const [saajanMessage, setSaajanMessage] = useState(
- "I didn't think I'd be here either.\nAnd yet, here we are.",
- );
+ const navigate = useNavigate();
+ const [isLoading, setIsLoading] = useState(false);
+ const [apiError, setApiError] = useState(null);
+ const [saajanMessage, setSaajanMessage] = useState(
+ "I didn't think I'd be here either.\nAnd yet, here we are.",
+ );
- const {
- register,
- handleSubmit,
- formState: { errors },
- } = useForm({
- resolver: zodResolver(registerSchema),
- });
+ const {
+ register,
+ handleSubmit,
+ formState: { errors },
+ } = useForm({
+ resolver: zodResolver(registerSchema),
+ });
- const onSubmit = async (data: RegisterInputs) => {
- setSaajanMessage("Good. I'll remember that.");
- setIsLoading(true);
- setApiError(null);
- try {
- // we generate the key bundle here to get the authHash (password) to be haSHed and stored in the db.
- const { authHash } = await CryptoUtils.deriveKeyBundle(
- data.password,
- data.email,
- );
+ const onSubmit = async (data: RegisterInputs) => {
+ setSaajanMessage("Good. I'll remember that.");
+ setIsLoading(true);
+ setApiError(null);
+ try {
+ // we generate the key bundle here to get the authHash (password) to be haSHed and stored in the db.
+ const { authHash } = await CryptoUtils.deriveKeyBundle(
+ data.password,
+ data.email,
+ );
- await publicApi.post(endpoints.REGISTER, {
- full_name: data.full_name,
- email: data.email,
- password: authHash,
- });
- navigate(ROUTES.VERIFY_EMAIL, { replace: true });
- } catch (err) {
- let message = "Registration failed. Please try again.";
- if (axios.isAxiosError(err)) {
- message = err.response?.data?.message || message;
- }
- setApiError(message);
- } finally {
- setIsLoading(false);
- }
- };
+ await publicApi.post(endpoints.REGISTER, {
+ full_name: data.full_name,
+ email: data.email,
+ password: authHash,
+ });
+ navigate(ROUTES.VERIFY_EMAIL, { replace: true });
+ } catch (err) {
+ let message = "Registration failed. Please try again.";
+ if (axios.isAxiosError(err)) {
+ message = err.response?.data?.message || message;
+ }
+ setApiError(message);
+ } finally {
+ setIsLoading(false);
+ }
+ };
- return (
-
-
-
-
+ );
}