Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
869eb16
Move editor constants to separate file
jzgom067 Jun 19, 2026
b198bbf
Remove export on unused export
jzgom067 Jun 19, 2026
70dce17
Remove event details placeholders
jzgom067 Jun 19, 2026
c80cf2f
Update painting loading page
jzgom067 Jun 19, 2026
b44f529
Update results loading page spacing
jzgom067 Jun 19, 2026
ab798be
Update event editor loading page
jzgom067 Jun 19, 2026
0d9aece
Fix mobile event editor loading title length
jzgom067 Jun 19, 2026
7d2b890
Move settings pages into subfolder
jzgom067 Jun 19, 2026
4394c3c
Create loading layout for settings page
jzgom067 Jun 19, 2026
f3bfa20
Merge pull request #348 from plan-cake/fix-circular-dependency
mirmirmirr Jun 20, 2026
acd4243
Update password criteria animation
jzgom067 Jun 20, 2026
f20ef4a
Update text alignment
jzgom067 Jun 20, 2026
dae10bb
Update strong password wording
jzgom067 Jun 20, 2026
b5ed37a
Update dashboard loading screen colors
jzgom067 Jun 20, 2026
a94fa28
Merge pull request #349 from plan-cake/loading-updates
mirmirmirr Jun 22, 2026
5e7277c
Merge pull request #350 from plan-cake/password-criteria-animation
mirmirmirr Jun 22, 2026
db88abd
Update code checking rate limit
jzgom067 Jun 22, 2026
d4d975a
Add cursor-pointer to toast X
jzgom067 Jun 22, 2026
d61df19
Create global.d.ts
jzgom067 Jun 22, 2026
a40d6e6
Add delay to state change on drawer tap
jzgom067 Jun 22, 2026
52aac47
Add floating drawer timeout cleanup
jzgom067 Jun 23, 2026
421c5b0
Remove DEBUG logs from production
jzgom067 Jun 23, 2026
389c223
Add automatic drawer for confirmation on mobile
jzgom067 Jun 23, 2026
6df6b6a
Merge pull request #351 from plan-cake/code-check-limit-adjustment
mirmirmirr Jun 24, 2026
8e495a4
Merge pull request #352 from plan-cake/toast-x-fix
mirmirmirr Jun 24, 2026
38123ba
Merge pull request #353 from plan-cake/css-import-fix
mirmirmirr Jun 24, 2026
12cb26f
Merge pull request #356 from plan-cake/token-log-removal
mirmirmirr Jun 24, 2026
cb6ccb0
Merge pull request #354 from plan-cake/mobile-results-footer-fix
mirmirmirr Jun 24, 2026
3ef5946
Merge pull request #357 from plan-cake/grid-page-popup-fix
mirmirmirr Jun 24, 2026
81f96e8
Add version data
jzgom067 Jun 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions backend/api/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class ThrottleScopes:
ThrottleScopes.PASSWORD_RESET.key: "10/hour",
ThrottleScopes.EVENT_CREATION.key: "25/hour",
ThrottleScopes.AVAILABILITY_ADD.key: "50/hour",
ThrottleScopes.CODE_CHECK.key: "50/hour",
ThrottleScopes.CODE_CHECK.key: "10/min",
},
}

Expand Down Expand Up @@ -195,7 +195,7 @@ class ThrottleScopes:
"class": "logging.handlers.RotatingFileHandler",
"filename": f"{LOG_DIR}/django.log",
"formatter": "verbose",
"level": "DEBUG",
"level": "DEBUG" if DEBUG else "INFO",
"maxBytes": 1024 * 1024 * 5, # 5 MB
"backupCount": 5,
},
Expand Down
11 changes: 6 additions & 5 deletions frontend/src/app/(event)/[event-code]/edit/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default function Loading() {
<HeaderSpacer />

<div className="flex w-full flex-wrap items-center justify-between md:flex-row">
<div className="bg-loading h-12 w-3/4 rounded-3xl md:w-1/2" />
<div className="bg-loading h-12 w-full rounded-3xl md:w-1/2" />
<div className="w-50 bg-loading hidden h-10 rounded-full" />
</div>

Expand All @@ -15,11 +15,12 @@ export default function Loading() {
<div className="bg-loading col-span-3 hidden h-20 rounded-3xl md:block" />
<div className="bg-loading hidden h-20 rounded-3xl md:col-start-1 md:row-start-2 md:block" />
<div className="h-120 bg-loading hidden rounded-3xl md:col-span-10 md:col-start-2 md:row-span-10 md:row-start-2 md:block" />
<div className="bg-loading hidden h-60 rounded-3xl md:col-start-1 md:row-start-11 md:block" />
<div className="bg-loading hidden h-10 rounded-3xl md:col-start-1 md:row-start-11 md:block" />

<div className="bg-loading h-20 w-3/4 rounded-3xl md:hidden" />
<div className="bg-loading h-20 w-3/4 rounded-3xl md:hidden" />
<div className="h-100 bg-loading rounded-3xl md:hidden" />
<div className="bg-loading h-16 w-3/4 rounded-3xl md:hidden" />
<div className="bg-loading h-24 w-3/4 rounded-3xl md:hidden" />
<div className="bg-loading h-24 w-3/4 rounded-3xl md:hidden" />
<div className="bg-loading h-10 w-3/4 rounded-3xl md:hidden" />
</div>
</div>
);
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/app/(event)/[event-code]/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@ export default function Loading() {
<div className="flex w-full flex-wrap items-center justify-between md:flex-row">
<div className="bg-loading h-12 w-1/2 rounded-3xl" />

<div className="w-50 bg-loading h-10 rounded-full" />
<div className="w-50 bg-loading hidden h-10 rounded-full md:block" />
</div>

<div className="mb-8 flex h-full flex-col gap-4 md:mb-0 md:flex-row">
<div className="bg-loading h-96 w-full rounded-3xl" />

<div className="h-fit w-full shrink-0 space-y-4 overflow-y-auto md:w-80">
<div className="bg-loading hidden h-20 rounded-3xl md:block" />
<div className="bg-loading hidden h-40 rounded-3xl md:block" />

<div className="h-70 bg-loading hidden rounded-3xl md:block" />
<div className="bg-loading h-20 rounded-3xl" />
</div>
</div>
Expand Down
9 changes: 5 additions & 4 deletions frontend/src/app/(event)/[event-code]/painting/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@ export default function Loading() {
<div className="flex w-full flex-wrap items-center justify-between md:flex-row">
<div className="bg-loading h-12 w-1/2 rounded-3xl" />

<div className="bg-loading h-10 w-40 rounded-full" />
<div className="bg-loading hidden h-10 w-40 rounded-full md:block" />
</div>

<div className="mb-8 flex h-full flex-col gap-4 md:mb-0 md:flex-row">
<div className="h-fit w-full shrink-0 space-y-6 overflow-y-auto md:w-80">
<div className="hidden h-fit w-full shrink-0 space-y-6 overflow-y-auto md:block md:w-80">
<div className="space-y-3">
<div className="bg-loading h-8 w-3/4 rounded-3xl" />
<div className="bg-loading h-8 w-1/2 rounded-3xl" />
</div>
<div className="h-70 bg-loading hidden rounded-3xl md:block" />
<div className="bg-loading h-20 rounded-3xl" />
<div className="bg-loading hidden h-20 rounded-3xl md:block" />
</div>

<div className="bg-loading h-96 w-full rounded-3xl" />

<div className="bg-loading h-20 rounded-3xl" />
</div>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/dashboard/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default function Loading() {

<div className="bg-panel w-full rounded-3xl">
<div className="p-4">
<div className="bg-foreground/10 h-10 w-1/2 animate-pulse rounded-full" />
<div className="bg-background h-10 w-1/2 animate-pulse rounded-full" />
Comment thread
jzgom067 marked this conversation as resolved.
</div>

{/* 2. Skeleton for the Event Grid */}
Expand All @@ -17,7 +17,7 @@ export default function Loading() {
{[1, 2, 3].map((i) => (
<div
key={i}
className="bg-foreground/5 h-50 w-full animate-pulse rounded-lg"
className="bg-background h-50 w-full animate-pulse rounded-lg"
/>
))}
</div>
Expand Down
21 changes: 21 additions & 0 deletions frontend/src/app/settings/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import HeaderSpacer from "@/features/header/components/header-spacer";

export default function Loading() {
return (
<div className="flex animate-pulse flex-col space-y-4 pl-6 pr-6">
<HeaderSpacer />

<div className="bg-background flex w-full flex-col gap-1">
<h1 className="text-2xl font-bold tracking-tight">Account Settings</h1>
<p className="text-foreground/60 text-sm">
Manage your account settings and preferences!
</p>
</div>

<div className="mt-2 flex flex-col gap-6 md:flex-row md:gap-12">
<div className="bg-loading w-23 h-11 rounded-3xl md:w-64" />
<div className="bg-loading h-68 w-full max-w-2xl rounded-3xl" />
</div>
</div>
);
}
13 changes: 4 additions & 9 deletions frontend/src/components/text-input-field.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useEffect, useRef, useState } from "react";

import * as Collapsible from "@radix-ui/react-collapsible";
import { EyeClosedIcon, EyeIcon, TriangleAlertIcon } from "lucide-react";

import PasswordCriteria from "@/features/auth/components/password-criteria";
Expand Down Expand Up @@ -192,14 +191,10 @@ export default function TextInputField(props: TextInputFieldProps) {
)}
</div>

{/* Password Criteria */}
<Collapsible.Root open={showPasswordCriteria}>
<Collapsible.Content className="collapsible-content">
<div className="px-4 pb-1 pt-2">
<PasswordCriteria criteria={passwordCriteria} />
</div>
</Collapsible.Content>
</Collapsible.Root>
<PasswordCriteria
open={showPasswordCriteria}
criteria={passwordCriteria}
/>
</div>
);
}
83 changes: 54 additions & 29 deletions frontend/src/features/auth/components/password-criteria.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,69 @@
import { AnimatePresence, motion } from "framer-motion";
import { CheckIcon, XIcon } from "lucide-react";

import { cn } from "@/lib/utils/classname";

type PasswordCriteriaProps = {
open: boolean;
criteria: { [key: string]: boolean };
};

export default function PasswordCriteria(props: PasswordCriteriaProps) {
const allCriteriaMet = Object.values(props.criteria).every((value) => value);
export default function PasswordCriteria({
open,
criteria,
}: PasswordCriteriaProps) {
const allCriteriaMet = Object.values(criteria).every((value) => value);

Comment thread
jzgom067 marked this conversation as resolved.
const iconClass = "w-4 h-4";

return (
<div className="w-full text-sm">
{allCriteriaMet ? (
<div className="flex items-center gap-1">
<CheckIcon className={iconClass} />
<b>Password is strong!</b>
</div>
) : (
<>
<b>Your password must:</b>
{Object.entries(props.criteria).map(([key, value], index) => (
<div
key={index}
className={cn(
"flex items-center gap-1",
value ? "line-through opacity-50" : "",
)}
>
{value ? (
<CheckIcon className={iconClass} />
) : (
<XIcon className={iconClass} />
)}
{key}
</div>
))}
</>
<AnimatePresence>
{open && (
<motion.div
className="w-full overflow-hidden px-4 text-sm"
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
>
<div className="pt-2 text-start">
<AnimatePresence mode="sync">
<motion.div
key={allCriteriaMet ? "strong" : "criteria"}
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
exit={{ opacity: 0, height: 0 }}
>
{allCriteriaMet ? (
<div className="flex items-center gap-1">
<CheckIcon className={iconClass} />
<b>Your password is strong!</b>
</div>
) : (
<>
<b>Your password must:</b>
{Object.entries(criteria).map(([key, value], index) => (
<div
key={index}
className={cn(
"flex items-center gap-1",
value ? "line-through opacity-50" : "",
)}
>
{value ? (
<CheckIcon className={iconClass} />
) : (
<XIcon className={iconClass} />
)}
{key}
</div>
))}
</>
)}
</motion.div>
</AnimatePresence>
</div>
</motion.div>
)}
</div>
</AnimatePresence>
);
}
22 changes: 20 additions & 2 deletions frontend/src/features/drawer/components/base.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useRef, useState } from "react";
import { useEffect, useRef, useState } from "react";

import { XIcon } from "lucide-react";
import { Drawer } from "vaul";
Expand Down Expand Up @@ -35,11 +35,21 @@ export default function BaseDrawer({

const contentRef = useRef<HTMLDivElement>(null);
const wasDraggingRef = useRef(false);
const animatingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const [isDragging, setIsDragging] = useState(false);
const [isAnimating, setIsAnimating] = useState(false);

useVaulStickyFooter(contentRef, isDragging || isAnimating);

// Animating timeout cleanup
useEffect(() => {
return () => {
if (animatingTimeoutRef.current) {
clearTimeout(animatingTimeoutRef.current);
}
};
}, []);

/**
* CONDITIONAL PROPS BASED ON VARIANT
*
Expand Down Expand Up @@ -188,9 +198,17 @@ export default function BaseDrawer({
onClick={() => {
if (wasDraggingRef.current) return;
if (isPill) {
if (animatingTimeoutRef.current) {
clearTimeout(animatingTimeoutRef.current);
}

setIsAnimating(true);
setSnap(snapPoints?.[1] ?? null);
setIsAnimating(false);
// Let the footer observer update before setting isAnimating to false
animatingTimeoutRef.current = setTimeout(() => {
setIsAnimating(false);
animatingTimeoutRef.current = null;
}, 0);
}
}}
className={cn(
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/features/event/editor/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const MAX_DAYS = 64;
export const MAX_DURATION_MS = MAX_DAYS * 24 * 60 * 60 * 1000;
export const MAX_DURATION = `${MAX_DAYS} days`;
export const MAX_TITLE_LENGTH = 50;
10 changes: 4 additions & 6 deletions frontend/src/features/event/editor/validate-data.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { EventInformation, WeekdayRange } from "@/core/event/types";
import { findRangeFromWeekdayArray } from "@/core/event/weekday-utils";
import {
MAX_DURATION_MS,
MAX_TITLE_LENGTH,
} from "@/features/event/editor/constants";
import { EventEditorType } from "@/features/event/editor/types";
import { MESSAGES } from "@/lib/messages";

const MAX_DAYS = 64;
export const MAX_DURATION_MS = MAX_DAYS * 24 * 60 * 60 * 1000;
export const MAX_DURATION = `${MAX_DAYS} days`;

export const MAX_TITLE_LENGTH = 50;

export async function validateEventData(
editorType: EventEditorType,
data: EventInformation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import ActionButton from "@/features/button/components/action";
import BaseModal from "@/features/system-feedback/dialog/components/base";
import { DIALOG_CONFIG } from "@/features/system-feedback/dialog/config";
import { ConfirmationDialogProps } from "@/features/system-feedback/dialog/props";
import useCheckMobile from "@/lib/hooks/use-check-mobile";
import { cn } from "@/lib/utils/classname";

export default function ConfirmationDialog({
Expand All @@ -26,6 +27,11 @@ export default function ConfirmationDialog({
const isControlled = controlledOpen !== undefined;
const open = isControlled ? controlledOpen : uncontrolledOpen;

const isMobile = useCheckMobile();
if (isMobile && !asNestedDrawer) {
asNestedDrawer = true;
}

const handleOpenChange = useCallback(
(newOpen: boolean) => {
if (!isControlled) {
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/features/system-feedback/toast/base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ export default function BaseToast({
}
}}
className={cn(
"z-10 col-start-3 row-span-2 flex h-6 w-6 items-center justify-center rounded-full",
"z-10 col-start-3 row-span-2 flex h-6 w-6",
"cursor-pointer items-center justify-center rounded-full",
"hover:bg-black/20 focus:outline-none focus:ring-2 focus:ring-white/50",
!isPersistent &&
"opacity-0 focus-visible:opacity-100 group-hover:opacity-100",
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/features/version-history/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,17 @@ export function getVersionHistoryData(): VersionHistoryData {
"Removed default dates and times from the new event page",
],
},
{
version: "v0.4.5",
releaseDate: { year: 2026, month: 5, day: 29 },
changes: [
"Added animations to the password strength criteria",
"Updated confirmation dialogs to display as drawers on mobile",
"Updated loading page skeletons to be consistent with new page layouts",
"Updated the authentication code checking rate limit to prevent lockouts",
"Fixed an issue where the mobile results page drawer would not display the footer when opened",
],
Comment thread
jzgom067 marked this conversation as resolved.
},
],
},
];
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Silences an error when importing globals.css in layout.tsx
declare module "*.css";
2 changes: 1 addition & 1 deletion frontend/src/lib/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { MAX_DEFAULT_NAME_LENGTH } from "@/features/account/constants";
import {
MAX_DURATION,
MAX_TITLE_LENGTH,
} from "@/features/event/editor/validate-data";
} from "@/features/event/editor/constants";

export const MESSAGES = {
// generic errors
Expand Down
Loading