Skip to content

Commit 19b6d88

Browse files
committed
Dialog validation / UX improvements
1 parent 12f1f80 commit 19b6d88

File tree

2 files changed

+86
-36
lines changed

2 files changed

+86
-36
lines changed

src/components/vault/dialogs/create-password-dialog.tsx

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
1-
"use client"
1+
"use client";
22

3-
import {createPasswordItem} from "@/app/actions";
4-
import {Button} from "@/components/ui/button";
5-
import {Dialog,DialogContent,DialogFooter,DialogHeader,DialogTitle} from "@/components/ui/dialog";
6-
import {Input} from "@/components/ui/input";
7-
import {encrypt} from "@/utils/encryption";
8-
import {useUser} from "@clerk/nextjs";
9-
import {Loader2} from "lucide-react";
10-
import {useState} from "react";
3+
import { createPasswordItem } from "@/app/actions";
4+
import { Button } from "@/components/ui/button";
5+
import {
6+
Dialog,
7+
DialogContent,
8+
DialogFooter,
9+
DialogHeader,
10+
DialogTitle,
11+
} from "@/components/ui/dialog";
12+
import { Input } from "@/components/ui/input";
13+
import { encrypt } from "@/utils/encryption";
14+
import { useUser } from "@clerk/nextjs";
15+
import { Loader2 } from "lucide-react";
16+
import { useState } from "react";
1117
import toast from "react-hot-toast";
12-
import {z} from "zod";
18+
import { z } from "zod";
1319

1420
export const CreatePasswordDialog = ({
1521
open,
@@ -38,34 +44,39 @@ export const CreatePasswordDialog = ({
3844
website: z
3945
.string()
4046
.url("Invalid website URL")
41-
.max(2048, "Website URL is too long"),
47+
.max(50, "Website URL is too long"),
4248
password: z
4349
.string()
44-
.min(6, "Password must be at least 6 characters long")
50+
.min(2, "Password must be at least 2 characters long")
4551
.max(128, "Password must be at most 128 characters"),
4652
});
4753

4854
const handleSave = async () => {
4955
setLoading(true);
56+
57+
const formattedWebsite = website.startsWith("https://")
58+
? website
59+
: `https://${website}`;
60+
5061
const validationResult = passwordSchema.safeParse({
5162
name,
5263
username,
53-
website,
64+
website: formattedWebsite,
5465
password,
5566
});
56-
67+
5768
if (!validationResult.success) {
5869
const errorMessage =
5970
validationResult.error.errors[0]?.message || "Validation failed";
6071
toast.error(errorMessage);
6172
setLoading(false);
6273
return;
6374
}
64-
75+
6576
try {
6677
await createPasswordItem(
6778
encrypt(username, clerkuser),
68-
encrypt(website, clerkuser),
79+
encrypt(formattedWebsite, clerkuser),
6980
encrypt(password, clerkuser)
7081
);
7182
toast.success("Password created");
@@ -76,6 +87,7 @@ export const CreatePasswordDialog = ({
7687
setLoading(false);
7788
}
7889
};
90+
7991

8092
return (
8193
<Dialog open={open} onOpenChange={onClose}>
@@ -91,7 +103,13 @@ export const CreatePasswordDialog = ({
91103
onChange={(e) => setName(e.target.value)}
92104
maxLength={50}
93105
/>
94-
<div className="mt-1 text-sm text-gray-500">{name.length} / 50</div>
106+
<div
107+
className="pointer-events-none absolute inset-y-0 end-0 flex items-center justify-center pe-3 text-xs tabular-nums text-muted-foreground peer-disabled:opacity-50"
108+
aria-live="polite"
109+
role="status"
110+
>
111+
{name.length}/50
112+
</div>
95113
</div>
96114
<div className="relative">
97115
<Input
@@ -100,19 +118,31 @@ export const CreatePasswordDialog = ({
100118
onChange={(e) => setUsername(e.target.value)}
101119
maxLength={30}
102120
/>
103-
<div className="mt-1 text-sm text-gray-500">
104-
{username.length} / 30
121+
<div
122+
className="pointer-events-none absolute inset-y-0 end-0 flex items-center justify-center pe-3 text-xs tabular-nums text-muted-foreground peer-disabled:opacity-50"
123+
aria-live="polite"
124+
role="status"
125+
>
126+
{username.length}/30
105127
</div>
106128
</div>
107-
<div className="relative">
129+
<div className="flex relative rounded-lg">
130+
<span className="-z-10 inline-flex items-center rounded-s-lg border border-input bg-background px-3 text-sm text-muted-foreground">
131+
https://
132+
</span>
108133
<Input
109134
placeholder="Website"
135+
className="-ms-px h-12 rounded-tl-none rounded-bl-none"
110136
value={website}
111137
onChange={(e) => setWebsite(e.target.value)}
112-
maxLength={1024}
138+
maxLength={50}
113139
/>
114-
<div className="mt-1 text-sm text-gray-500">
115-
{website.length} / 1024
140+
<div
141+
className="pointer-events-none absolute inset-y-0 end-0 flex items-center justify-center pe-3 text-xs tabular-nums text-muted-foreground peer-disabled:opacity-50"
142+
aria-live="polite"
143+
role="status"
144+
>
145+
{website.length}/50
116146
</div>
117147
</div>
118148
<div className="relative">
@@ -123,8 +153,12 @@ export const CreatePasswordDialog = ({
123153
type="password"
124154
maxLength={128}
125155
/>
126-
<div className=" mt-1 text-sm text-gray-500">
127-
{password.length} / 128
156+
<div
157+
className="pointer-events-none absolute inset-y-0 end-0 flex items-center justify-center pe-3 text-xs tabular-nums text-muted-foreground peer-disabled:opacity-50"
158+
aria-live="polite"
159+
role="status"
160+
>
161+
{password.length}/128
128162
</div>
129163
</div>
130164
</div>

src/components/vault/dialogs/edit-password-dialog.tsx

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ export function EditPasswordDialog({
6060
website: z
6161
.string()
6262
.url("Invalid website URL")
63-
.max(2048, "Website URL is too long"),
63+
.max(50, "Website URL is too long"),
6464
password: z
6565
.string()
66-
.min(6, "Password must be at least 6 characters long")
66+
.min(2, "Password must be at least 2 characters long")
6767
.max(128, "Password must be at most 128 characters"),
6868
});
6969

@@ -137,8 +137,12 @@ export function EditPasswordDialog({
137137
onChange={handleInputChange}
138138
maxLength={50}
139139
/>
140-
<div className="mt-1 text-sm text-gray-500">
141-
{editedEntry.name.length} / 50
140+
<div
141+
className="pointer-events-none absolute inset-y-0 end-0 flex items-center justify-center pe-3 text-xs tabular-nums text-muted-foreground peer-disabled:opacity-50"
142+
aria-live="polite"
143+
role="status"
144+
>
145+
{editedEntry.name.length}/50
142146
</div>
143147
</div>
144148
<div className="relative">
@@ -148,19 +152,27 @@ export function EditPasswordDialog({
148152
onChange={handleInputChange}
149153
maxLength={30}
150154
/>
151-
<div className="mt-1 text-sm text-gray-500">
152-
{editedEntry.username.length} / 30
155+
<div
156+
className="pointer-events-none absolute inset-y-0 end-0 flex items-center justify-center pe-3 text-xs tabular-nums text-muted-foreground peer-disabled:opacity-50"
157+
aria-live="polite"
158+
role="status"
159+
>
160+
{editedEntry.username.length}/30
153161
</div>
154162
</div>
155163
<div className="relative">
156164
<Input
157165
placeholder="Website"
158166
value={editedEntry.website}
159167
onChange={handleInputChange}
160-
maxLength={1024}
168+
maxLength={50}
161169
/>
162-
<div className="mt-1 text-sm text-gray-500">
163-
{editedEntry.website.length} / 1024
170+
<div
171+
className="pointer-events-none absolute inset-y-0 end-0 flex items-center justify-center pe-3 text-xs tabular-nums text-muted-foreground peer-disabled:opacity-50"
172+
aria-live="polite"
173+
role="status"
174+
>
175+
{editedEntry.website.length}/50
164176
</div>
165177
</div>
166178
<div className="relative">
@@ -171,8 +183,12 @@ export function EditPasswordDialog({
171183
type="password"
172184
maxLength={128}
173185
/>
174-
<div className=" mt-1 text-sm text-gray-500">
175-
{editedEntry.password.length} / 128
186+
<div
187+
className="pointer-events-none absolute inset-y-0 end-0 flex items-center justify-center pe-3 text-xs tabular-nums text-muted-foreground peer-disabled:opacity-50"
188+
aria-live="polite"
189+
role="status"
190+
>
191+
{editedEntry.password.length}/128
176192
</div>
177193
</div>
178194
</div>

0 commit comments

Comments
 (0)