Skip to content

Commit

Permalink
add: translated error warnings
Browse files Browse the repository at this point in the history
  • Loading branch information
David Hordiienko committed Jun 27, 2024
1 parent 13a06c6 commit 9667126
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 21 deletions.
43 changes: 29 additions & 14 deletions backend/src/lib/common-schemas.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { config } from 'config';
import { z } from 'zod';
import { t } from './utils';

export const passwordSchema = z.string().min(8).max(100);

Expand Down Expand Up @@ -44,8 +45,16 @@ export const paginationQuerySchema = z.object({
q: z.string().optional(),
sort: z.enum(['createdAt']).default('createdAt').optional(),
order: z.enum(['asc', 'desc']).default('asc').optional(),
offset: z.string().default('0').optional().refine(offsetRefine, 'Must be number greater or equal to 0'),
limit: z.string().default('50').optional().refine(limitRefine, 'Must be number greater than 0'),
offset: z
.string()
.default('0')
.optional()
.refine(offsetRefine, { message: t('invalid.min_length_greater_or_eq', { length: 0 }) }),
limit: z
.string()
.default('50')
.optional()
.refine(limitRefine, { message: t('invalid.min_length_greater', { length: 0 }) }),
});

export const idsQuerySchema = z.object({
Expand All @@ -56,10 +65,9 @@ export const validSlugSchema = z
.string()
.min(2)
.max(100)
.refine(
(s) => /^[a-z0-9]+(-{0,3}[a-z0-9]+)*$/i.test(s),
'Slug may only contain alphanumeric characters or up to three hyphens, and cannot begin or end with a hyphen.',
)
.refine((s) => /^[a-z0-9]+(-{0,3}[a-z0-9]+)*$/i.test(s), {
message: t('invalid.slug'),
})
.transform((str) => str.toLowerCase().trim());

export const validDomainsSchema = z
Expand All @@ -68,10 +76,9 @@ export const validDomainsSchema = z
.string()
.min(4)
.max(100)
.refine(
(s) => /^[a-z0-9].*[a-z0-9]$/i.test(s) && s.includes('.'),
'Domain must not contain @, no special chars and at least one dot (.) in between.',
)
.refine((s) => /^[a-z0-9].*[a-z0-9]$/i.test(s) && s.includes('.'), {
message: t('invalid.domain'),
})
.transform((str) => str.toLowerCase().trim()),
)
.optional();
Expand All @@ -91,18 +98,26 @@ export const membershipsCountSchema = z.object({
export const imageUrlSchema = z
.string()
.url()
.refine((url) => new URL(url).search === '', 'Search params not allowed');
.refine((url) => new URL(url).search === '', {
message: t('invalid.image_url'),
});

export const nameSchema = z
.string()
.min(2)
.max(100)
.refine((s) => /^[a-z0-9 ,.'-]+$/i.test(s), "Name may only contain letters, numbers, spaces and these characters: ,.'-");
.refine((s) => /^[a-z0-9 ,.'-]+$/i.test(s), {
message: t('invalid.name'),
});

export const colorSchema = z
.string()
.min(3)
.max(7)
.regex(/^#(?:[0-9a-fA-F]{3}){1,2}$/, 'Color may only contain letters, numbers & starts with #');
.regex(/^#(?:[0-9a-fA-F]{3}){1,2}$/, {
message: t('invalid.color'),
});

export const validUrlSchema = z.string().refine((url: string) => url.startsWith('https'), 'URL must start with https://');
export const validUrlSchema = z.string().refine((url: string) => url.startsWith('https'), {
message: t('invalid.url'),
});
6 changes: 6 additions & 0 deletions backend/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { env } from 'env';
import { sign } from 'hono/jwt';
import { i18n } from './i18n';
import { config } from 'config';

interface GenerateTokenOptions {
userId: string;
Expand All @@ -24,3 +26,7 @@ export const generateElectricJWTToken = async ({ userId }: GenerateTokenOptions)
'ES256',
);
};

export const { t } = i18n.cloneInstance({
lng: config.defaultLanguage,
});
3 changes: 2 additions & 1 deletion backend/src/modules/general/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from '../../lib/common-schemas';
import { membershipInfoSchema } from '../memberships/schema';
import { userSchema } from '../users/schema';
import { t } from '../../lib/utils';

export const publicCountsSchema = z.object({
users: z.number(),
Expand All @@ -33,7 +34,7 @@ export const checkTokenSchema = z.object({
});

export const inviteBodySchema = z.object({
emails: userSchema.shape.email.array().min(1),
emails: userSchema.shape.email.array().min(1, { message: t('invalid.min_items', { items_count: 'one', item: 'email' }) }),
role: userSchema.shape.role,
});

Expand Down
3 changes: 2 additions & 1 deletion backend/src/modules/memberships/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createSelectSchema } from 'drizzle-zod';
import { membershipsTable } from '../../db/schema/memberships';
import { contextEntityTypeSchema, idOrSlugSchema, idSchema, idsQuerySchema } from '../../lib/common-schemas';
import { userSchema } from '../users/schema';
import { t } from '../../lib/utils';

const membershipTableSchema = createSelectSchema(membershipsTable);

Expand All @@ -15,7 +16,7 @@ export const membershipSchema = membershipTableSchema.extend({
});

export const createMembershipBodySchema = z.object({
emails: userSchema.shape.email.array().min(1),
emails: userSchema.shape.email.array().min(1, { message: t('invalid.min_items', { items_count: 'one', item: 'email' }) }),
role: membershipSchema.shape.role,
});

Expand Down
6 changes: 5 additions & 1 deletion backend/src/modules/organizations/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
validUrlSchema,
} from '../../lib/common-schemas';
import { membershipInfoSchema } from '../memberships/schema';
import { t } from '../../lib/utils';

export const organizationSchema = z.object({
...createSelectSchema(organizationsTable).shape,
Expand All @@ -33,7 +34,10 @@ export const updateOrganizationBodySchema = createInsertSchema(organizationsTabl
slug: validSlugSchema,
name: nameSchema,
shortName: nameSchema,
languages: z.array(z.string()).min(1).optional(),
languages: z
.array(z.string())
.min(1, { message: t('invalid.min_items', { items_count: 'one', item: 'language' }) })
.optional(),
emailDomains: validDomainsSchema,
authStrategies: z.array(z.string()).optional(),
websiteUrl: validUrlSchema,
Expand Down
6 changes: 5 additions & 1 deletion backend/src/modules/requests/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import { z } from 'zod';
import { createSelectSchema } from 'drizzle-zod';
import { requestsTable } from '../../db/schema/requests';
import { paginationQuerySchema } from '../../lib/common-schemas';
import { t } from '../../lib/utils';

const requestsTableSchema = createSelectSchema(requestsTable);

export const requestsSchema = z.object({
email: z.string().min(1).email(),
email: z
.string()
.email(t('invalid.email'))
.min(1, { message: t('required') }),
type: requestsTableSchema.shape.type,
});

Expand Down
3 changes: 2 additions & 1 deletion frontend/src/modules/users/invite-email-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Button } from '~/modules/ui/button';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '~/modules/ui/form';
import type { EntityPage } from '~/types';
import { idOrSlugSchema } from 'backend/lib/common-schemas';
import { t } from 'backend/lib/utils';

interface Props {
entity?: EntityPage;
Expand All @@ -29,7 +30,7 @@ interface Props {
}

const formSchema = z.object({
emails: z.array(z.string().email('Invalid email')).min(1),
emails: z.array(z.string().email(t('invalid.email'))).min(1, { message: t('invalid.min_items', { items_count: 'one', item: 'email' }) }),
role: z.enum(config.rolesByType.allRoles),
idOrSlug: idOrSlugSchema.optional(),
});
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/modules/users/invite-search-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { Button } from '~/modules/ui/button';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '~/modules/ui/form';
import type { EntityPage } from '~/types';
import { QueryCombobox } from '~/modules/common/query-combobox';
import { t } from 'backend/lib/utils';

interface Props {
entity?: EntityPage;
Expand All @@ -26,7 +27,7 @@ interface Props {
}

const formSchema = z.object({
emails: z.array(z.string().email('Invalid email')).min(1),
emails: z.array(z.string().email(t('invalid.email'))).min(1, { message: t('invalid.min_items', { items_count: 'one', item: 'email' }) }),
role: z.enum(config.rolesByType.entityRoles).optional(),
idOrSlug: idOrSlugSchema.optional(),
});
Expand Down
14 changes: 13 additions & 1 deletion locales/en/backend.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,17 @@
"email.reset_password_text_3": "Ignore this email if you don't want to change your password or didn't request it.",
"email.reset_password_text_2": "This link will expire in 3 hours. To get a new password reset link, visit:",
"email.reset_password_text_1": "A password reset link has been requested for your Cella account. If this was you, you can set a new password here:",
"email.hi": "Hi"
"email.hi": "Hi",
"invalid.min_items": "Please add at least {{items_count}} {{item}} to the list.",
"invalid.min_length_greater": "Must be greater than {{length}}",
"invalid.min_length_greater_or_eq": "Must be greater or equal to {{length}}",
"invalid.max_length": "{{prefix}} be at most {{length}} characters.",
"invalid.name": "Name may only contain letters, numbers, spaces and these characters: ,.'-",
"invalid.url": "URL must start with https://",
"invalid.image_url": "Search params not allowed",
"invalid.color": "Color may only contain letters, numbers & starts with #",
"invalid.domain": "Domain must not contain @, no special chars and at least one dot (.) in between.",
"invalid.slug": "Slug may only contain alphanumeric characters or up to three hyphens, and cannot begin or end with a hyphen.",
"invalid.email": "Invalid email address.",
"required": "This field is required."
}

0 comments on commit 9667126

Please sign in to comment.