form feedback consolidation

This commit is contained in:
Michael Freno
2026-01-06 14:00:22 -05:00
parent a11f1fee50
commit 374c924119
3 changed files with 82 additions and 70 deletions

View File

@@ -0,0 +1,27 @@
import { Show, type JSX } from "solid-js";
export interface FormFeedbackProps {
type: "success" | "error";
message: string | JSX.Element;
show?: boolean;
class?: string;
}
export default function FormFeedback(props: FormFeedbackProps) {
const show = () => props.show ?? true;
return (
<Show when={show()}>
<div
class={`text-center text-sm ${
props.type === "success" ? "text-green" : "text-red"
} ${props.class || ""}`}
role="alert"
>
{props.message}
</div>
</Show>
);
}
export { FormFeedback };

View File

@@ -15,6 +15,7 @@ import { api } from "~/lib/api";
import Input from "~/components/ui/Input"; import Input from "~/components/ui/Input";
import PasswordInput from "~/components/ui/PasswordInput"; import PasswordInput from "~/components/ui/PasswordInput";
import Button from "~/components/ui/Button"; import Button from "~/components/ui/Button";
import FormFeedback from "~/components/ui/FormFeedback";
import type { UserProfile } from "~/types/user"; import type { UserProfile } from "~/types/user";
import PasswordStrengthMeter from "~/components/PasswordStrengthMeter"; import PasswordStrengthMeter from "~/components/PasswordStrengthMeter";
@@ -611,11 +612,12 @@ export default function AccountPage() {
: "Set Image"} : "Set Image"}
</button> </button>
</form> </form>
<Show when={showImageSuccess()}> <FormFeedback
<div class="text-green mt-2 text-center text-sm"> type="success"
Profile image updated! message="Profile image updated!"
</div> show={showImageSuccess()}
</Show> class="mt-2"
/>
</div> </div>
</div> </div>
@@ -694,11 +696,12 @@ export default function AccountPage() {
Submit Submit
</Button> </Button>
</div> </div>
<Show when={showEmailSuccess()}> <FormFeedback
<div class="text-green mt-2 text-center text-sm"> type="success"
Email updated! message="Email updated!"
</div> show={showEmailSuccess()}
</Show> class="mt-2"
/>
</form> </form>
{/* Display Name Section */} {/* Display Name Section */}
@@ -741,11 +744,12 @@ export default function AccountPage() {
Submit Submit
</Button> </Button>
</div> </div>
<Show when={showDisplayNameSuccess()}> <FormFeedback
<div class="text-green mt-2 text-center text-sm"> type="success"
Display name updated! message="Display name updated!"
</div> show={showDisplayNameSuccess()}
</Show> class="mt-2"
/>
</form> </form>
</div> </div>
@@ -817,9 +821,11 @@ export default function AccountPage() {
newPasswordConfRef.value.length >= 6 newPasswordConfRef.value.length >= 6
} }
> >
<div class="text-red mb-4 text-center text-sm"> <FormFeedback
Passwords do not match! type="error"
</div> message="Passwords do not match!"
class="mb-4"
/>
</Show> </Show>
<Button <Button
@@ -831,20 +837,21 @@ export default function AccountPage() {
Set Set
</Button> </Button>
<Show when={passwordError()}> <FormFeedback
<div class="text-red text-center text-sm"> type="error"
{userProfile().hasPassword message={
userProfile().hasPassword
? "Password did not match record" ? "Password did not match record"
: "Error setting password"} : "Must have email & password provider linked or set password first"
</div> }
</Show> show={passwordError()}
/>
<Show when={showPasswordSuccess()}> <FormFeedback
<div class="text-green text-center text-sm"> type="success"
Password {userProfile().hasPassword ? "changed" : "set"}{" "} message={`Password ${userProfile().hasPassword ? "changed" : "set"} successfully!`}
successfully! show={showPasswordSuccess()}
</div> />
</Show>
</div> </div>
</form> </form>
@@ -924,11 +931,12 @@ export default function AccountPage() {
Delete Account Delete Account
</Button> </Button>
<Show when={passwordDeletionError()}> <FormFeedback
<div class="text-red mt-2 text-center text-sm"> type="error"
Password did not match record message="Password did not match record"
</div> show={passwordDeletionError()}
</Show> class="mt-2"
/>
</form> </form>
</Show> </Show>
</div> </div>

View File

@@ -7,6 +7,7 @@ import { getClientCookie } from "~/lib/cookies.client";
import { COUNTDOWN_CONFIG } from "~/config"; import { COUNTDOWN_CONFIG } from "~/config";
import Input from "~/components/ui/Input"; import Input from "~/components/ui/Input";
import { Button } from "~/components/ui/Button"; import { Button } from "~/components/ui/Button";
import FormFeedback from "~/components/ui/FormFeedback";
export default function RequestPasswordResetPage() { export default function RequestPasswordResetPage() {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -174,42 +175,18 @@ export default function RequestPasswordResetPage() {
</div> </div>
</form> </form>
<div <FormFeedback
class={`${ type="success"
showSuccessMessage() ? "" : "opacity-0 select-none" message="If email exists, you will receive an email shortly!"
} text-green flex justify-center italic transition-opacity duration-300 ease-in-out`} show={showSuccessMessage()}
> />
If email exists, you will receive an email shortly!
</div>
<Show when={error()}> <FormFeedback
<div class="mt-4 flex justify-center"> type="error"
<div message={error()}
class={`${ show={error() !== ""}
error().includes("Too many attempts") || class="mt-4"
error().includes("wait before requesting") />
? "border-maroon bg-red rounded-lg border px-4 py-3"
: ""
} max-w-md text-center`}
>
<Show when={error().includes("Too many attempts")}>
<div class="mb-1 text-base font-semibold">
Rate Limit Exceeded
</div>
</Show>
<div
class={`${
error().includes("Too many attempts") ||
error().includes("wait before requesting")
? "text-sm"
: "text-red text-sm italic"
}`}
>
{error()}
</div>
</div>
</div>
</Show>
<div class="mt-6 flex justify-center"> <div class="mt-6 flex justify-center">
<A <A