Snyk report fixes + offcanvas sidebar fix (#263)
Refactor: - Improved consistency and robustness by standardizing file encoding arguments from 'utf-8' to 'utf8' across various file read/write operations. - Simplified status mapping logic in billing components and services by replacing switch statements with direct mapping objects for clearer and more maintainable code. - Enhanced type conversion and error handling in billing and internationalization components for improved reliability. - Updated sorting logic in team member tables for more predictable member ordering. - Improved error logging with sanitized output to prevent formatting issues. - Adjusted environment variable whitelisting to use a more flexible matching pattern. - Fix variables for sidebar style handling Style: - Refined spacing and layout in account selector and sidebar header components for better visual consistency.
This commit is contained in:
committed by
GitHub
parent
cb80e4fdcf
commit
fc2fda595a
@@ -37,7 +37,7 @@ export async function updateTranslationAction(props: z.infer<typeof Schema>) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Read the current translations file
|
// Read the current translations file
|
||||||
const translationsFile = readFileSync(filePath, 'utf-8');
|
const translationsFile = readFileSync(filePath, 'utf8');
|
||||||
const translations = JSON.parse(translationsFile) as Record<string, any>;
|
const translations = JSON.parse(translationsFile) as Record<string, any>;
|
||||||
|
|
||||||
// Update the nested key value
|
// Update the nested key value
|
||||||
@@ -60,7 +60,7 @@ export async function updateTranslationAction(props: z.infer<typeof Schema>) {
|
|||||||
current[finalKey] = value;
|
current[finalKey] = value;
|
||||||
|
|
||||||
// Write the updated translations back to the file
|
// Write the updated translations back to the file
|
||||||
writeFileSync(filePath, JSON.stringify(translations, null, 2), 'utf-8');
|
writeFileSync(filePath, JSON.stringify(translations, null, 2), 'utf8');
|
||||||
|
|
||||||
revalidatePath(`/translations`);
|
revalidatePath(`/translations`);
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ export const translateWithAIAction = async (
|
|||||||
|
|
||||||
if (!existsSync(filePath)) {
|
if (!existsSync(filePath)) {
|
||||||
// create the file if it doesn't exist
|
// create the file if it doesn't exist
|
||||||
writeFileSync(filePath, JSON.stringify({}, null, 2), 'utf-8');
|
writeFileSync(filePath, JSON.stringify({}, null, 2), 'utf8');
|
||||||
}
|
}
|
||||||
|
|
||||||
const results: Record<string, string> = {};
|
const results: Record<string, string> = {};
|
||||||
|
|||||||
@@ -265,12 +265,12 @@ export const envVariables: EnvVariableModel[] = [
|
|||||||
{
|
{
|
||||||
name: 'NEXT_PUBLIC_SIDEBAR_COLLAPSIBLE_STYLE',
|
name: 'NEXT_PUBLIC_SIDEBAR_COLLAPSIBLE_STYLE',
|
||||||
description:
|
description:
|
||||||
'Defines sidebar collapse behavior. Options: offscreen, icon, or none.',
|
'Defines sidebar collapse behavior. Options: offcanvas, icon, or none.',
|
||||||
category: 'Navigation',
|
category: 'Navigation',
|
||||||
type: 'enum',
|
type: 'enum',
|
||||||
values: ['offscreen', 'icon', 'none'],
|
values: ['offcanvas', 'icon', 'none'],
|
||||||
validate: ({ value }) => {
|
validate: ({ value }) => {
|
||||||
return z.enum(['offscreen', 'icon', 'none']).optional().safeParse(value);
|
return z.enum(['offcanvas', 'icon', 'none']).optional().safeParse(value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -53,10 +53,10 @@ export async function updateEnvironmentVariableAction(
|
|||||||
const filePath = `${root}/apps/web/${source}`;
|
const filePath = `${root}/apps/web/${source}`;
|
||||||
|
|
||||||
if (!existsSync(filePath)) {
|
if (!existsSync(filePath)) {
|
||||||
writeFileSync(filePath, '', 'utf-8');
|
writeFileSync(filePath, '', 'utf8');
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourceEnvFile = readFileSync(`${root}apps/web/${source}`, 'utf-8');
|
const sourceEnvFile = readFileSync(`${root}apps/web/${source}`, 'utf8');
|
||||||
|
|
||||||
let updatedEnvFile = '';
|
let updatedEnvFile = '';
|
||||||
const isInSourceFile = sourceEnvFile.includes(name);
|
const isInSourceFile = sourceEnvFile.includes(name);
|
||||||
@@ -73,7 +73,7 @@ export async function updateEnvironmentVariableAction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// write the updated content back to the file
|
// write the updated content back to the file
|
||||||
writeFileSync(`${root}/apps/web/${source}`, updatedEnvFile, 'utf-8');
|
writeFileSync(`${root}/apps/web/${source}`, updatedEnvFile, 'utf8');
|
||||||
|
|
||||||
revalidatePath(`/variables`);
|
revalidatePath(`/variables`);
|
||||||
|
|
||||||
|
|||||||
@@ -43,5 +43,5 @@ export const personalAccountNavigationConfig = NavigationConfigSchema.parse({
|
|||||||
routes,
|
routes,
|
||||||
style: process.env.NEXT_PUBLIC_USER_NAVIGATION_STYLE,
|
style: process.env.NEXT_PUBLIC_USER_NAVIGATION_STYLE,
|
||||||
sidebarCollapsed: process.env.NEXT_PUBLIC_HOME_SIDEBAR_COLLAPSED,
|
sidebarCollapsed: process.env.NEXT_PUBLIC_HOME_SIDEBAR_COLLAPSED,
|
||||||
sidebarCollapsedStyle: process.env.NEXT_PUBLIC_SIDEBAR_COLLAPSED_STYLE,
|
sidebarCollapsedStyle: process.env.NEXT_PUBLIC_SIDEBAR_COLLAPSIBLE_STYLE,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export function getTeamAccountSidebarConfig(account: string) {
|
|||||||
routes: getRoutes(account),
|
routes: getRoutes(account),
|
||||||
style: process.env.NEXT_PUBLIC_TEAM_NAVIGATION_STYLE,
|
style: process.env.NEXT_PUBLIC_TEAM_NAVIGATION_STYLE,
|
||||||
sidebarCollapsed: process.env.NEXT_PUBLIC_TEAM_SIDEBAR_COLLAPSED,
|
sidebarCollapsed: process.env.NEXT_PUBLIC_TEAM_SIDEBAR_COLLAPSED,
|
||||||
sidebarCollapsedStyle: process.env.NEXT_PUBLIC_SIDEBAR_COLLAPSED_STYLE,
|
sidebarCollapsedStyle: process.env.NEXT_PUBLIC_SIDEBAR_COLLAPSIBLE_STYLE,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,43 +2,27 @@ import { Enums } from '@kit/supabase/database';
|
|||||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||||
import { Trans } from '@kit/ui/trans';
|
import { Trans } from '@kit/ui/trans';
|
||||||
|
|
||||||
|
const statusBadgeMap: Record<Enums<'subscription_status'>, `success` | `destructive` | `warning`> = {
|
||||||
|
active: 'success',
|
||||||
|
trialing: 'success',
|
||||||
|
past_due: 'destructive',
|
||||||
|
canceled: 'destructive',
|
||||||
|
unpaid: 'destructive',
|
||||||
|
incomplete: 'warning',
|
||||||
|
incomplete_expired: 'destructive',
|
||||||
|
paused: 'warning',
|
||||||
|
};
|
||||||
|
|
||||||
export function CurrentPlanAlert(
|
export function CurrentPlanAlert(
|
||||||
props: React.PropsWithoutRef<{
|
props: React.PropsWithoutRef<{
|
||||||
status: Enums<'subscription_status'>;
|
status: Enums<'subscription_status'>;
|
||||||
}>,
|
}>,
|
||||||
) {
|
) {
|
||||||
let variant: 'success' | 'warning' | 'destructive';
|
|
||||||
const prefix = 'billing:status';
|
const prefix = 'billing:status';
|
||||||
|
|
||||||
const text = `${prefix}.${props.status}.description`;
|
const text = `${prefix}.${props.status}.description`;
|
||||||
const title = `${prefix}.${props.status}.heading`;
|
const title = `${prefix}.${props.status}.heading`;
|
||||||
|
const variant = statusBadgeMap[props.status];
|
||||||
switch (props.status) {
|
|
||||||
case 'active':
|
|
||||||
variant = 'success';
|
|
||||||
break;
|
|
||||||
case 'trialing':
|
|
||||||
variant = 'success';
|
|
||||||
break;
|
|
||||||
case 'past_due':
|
|
||||||
variant = 'destructive';
|
|
||||||
break;
|
|
||||||
case 'canceled':
|
|
||||||
variant = 'destructive';
|
|
||||||
break;
|
|
||||||
case 'unpaid':
|
|
||||||
variant = 'destructive';
|
|
||||||
break;
|
|
||||||
case 'incomplete':
|
|
||||||
variant = 'warning';
|
|
||||||
break;
|
|
||||||
case 'incomplete_expired':
|
|
||||||
variant = 'destructive';
|
|
||||||
break;
|
|
||||||
case 'paused':
|
|
||||||
variant = 'warning';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Alert variant={variant}>
|
<Alert variant={variant}>
|
||||||
|
|||||||
@@ -4,43 +4,27 @@ import { Trans } from '@kit/ui/trans';
|
|||||||
|
|
||||||
type Status = Enums<'subscription_status'> | Enums<'payment_status'>;
|
type Status = Enums<'subscription_status'> | Enums<'payment_status'>;
|
||||||
|
|
||||||
|
const statusBadgeMap: Record<Status, `success` | `destructive` | `warning`> = {
|
||||||
|
active: 'success',
|
||||||
|
succeeded: 'success',
|
||||||
|
trialing: 'success',
|
||||||
|
past_due: 'destructive',
|
||||||
|
failed: 'destructive',
|
||||||
|
canceled: 'destructive',
|
||||||
|
unpaid: 'destructive',
|
||||||
|
incomplete: 'warning',
|
||||||
|
pending: 'warning',
|
||||||
|
incomplete_expired: 'destructive',
|
||||||
|
paused: 'warning',
|
||||||
|
}
|
||||||
|
|
||||||
export function CurrentPlanBadge(
|
export function CurrentPlanBadge(
|
||||||
props: React.PropsWithoutRef<{
|
props: React.PropsWithoutRef<{
|
||||||
status: Status;
|
status: Status;
|
||||||
}>,
|
}>,
|
||||||
) {
|
) {
|
||||||
let variant: 'success' | 'warning' | 'destructive';
|
|
||||||
const text = `billing:status.${props.status}.badge`;
|
const text = `billing:status.${props.status}.badge`;
|
||||||
|
const variant = statusBadgeMap[props.status];
|
||||||
switch (props.status) {
|
|
||||||
case 'active':
|
|
||||||
case 'succeeded':
|
|
||||||
variant = 'success';
|
|
||||||
break;
|
|
||||||
case 'trialing':
|
|
||||||
variant = 'success';
|
|
||||||
break;
|
|
||||||
case 'past_due':
|
|
||||||
case 'failed':
|
|
||||||
variant = 'destructive';
|
|
||||||
break;
|
|
||||||
case 'canceled':
|
|
||||||
variant = 'destructive';
|
|
||||||
break;
|
|
||||||
case 'unpaid':
|
|
||||||
variant = 'destructive';
|
|
||||||
break;
|
|
||||||
case 'incomplete':
|
|
||||||
case 'pending':
|
|
||||||
variant = 'warning';
|
|
||||||
break;
|
|
||||||
case 'incomplete_expired':
|
|
||||||
variant = 'destructive';
|
|
||||||
break;
|
|
||||||
case 'paused':
|
|
||||||
variant = 'warning';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Badge data-test={'current-plan-card-status-badge'} variant={variant}>
|
<Badge data-test={'current-plan-card-status-badge'} variant={variant}>
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ function Tiers({
|
|||||||
i18nKey={'billing:andAbove'}
|
i18nKey={'billing:andAbove'}
|
||||||
values={{
|
values={{
|
||||||
unit,
|
unit,
|
||||||
previousTier: (previousTierFrom as number) - 1,
|
previousTier: (Number(previousTierFrom) as number) - 1,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { BillingConfig } from '@kit/billing';
|
|
||||||
import { UpsertSubscriptionParams } from '@kit/billing/types';
|
import { UpsertSubscriptionParams } from '@kit/billing/types';
|
||||||
|
|
||||||
type SubscriptionStatus =
|
type SubscriptionStatus =
|
||||||
@@ -23,8 +22,6 @@ export function createLemonSqueezySubscriptionPayloadBuilderService() {
|
|||||||
* @description This class is used to build the subscription payload for Lemon Squeezy
|
* @description This class is used to build the subscription payload for Lemon Squeezy
|
||||||
*/
|
*/
|
||||||
class LemonSqueezySubscriptionPayloadBuilderService {
|
class LemonSqueezySubscriptionPayloadBuilderService {
|
||||||
private config?: BillingConfig;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name build
|
* @name build
|
||||||
* @description Build the subscription payload for Lemon Squeezy
|
* @description Build the subscription payload for Lemon Squeezy
|
||||||
@@ -103,24 +100,18 @@ class LemonSqueezySubscriptionPayloadBuilderService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getSubscriptionStatus(status: SubscriptionStatus) {
|
private getSubscriptionStatus(status: SubscriptionStatus) {
|
||||||
switch (status) {
|
const statusMap = {
|
||||||
case 'active':
|
active: 'active',
|
||||||
return 'active';
|
cancelled: 'canceled',
|
||||||
case 'cancelled':
|
paused: 'paused',
|
||||||
return 'canceled';
|
on_trial: 'trialing',
|
||||||
case 'paused':
|
past_due: 'past_due',
|
||||||
return 'paused';
|
unpaid: 'unpaid',
|
||||||
case 'on_trial':
|
expired: 'past_due',
|
||||||
return 'trialing';
|
} satisfies Record<SubscriptionStatus, UpsertSubscriptionParams['status']>;
|
||||||
case 'past_due':
|
|
||||||
return 'past_due';
|
// Default to 'active' if status is unknown
|
||||||
case 'unpaid':
|
return statusMap[status] || 'active';
|
||||||
return 'unpaid';
|
|
||||||
case 'expired':
|
|
||||||
return 'past_due';
|
|
||||||
default:
|
|
||||||
return 'active';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -447,19 +447,16 @@ export class LemonSqueezyWebhookHandlerService
|
|||||||
|
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOrderStatus(status: OrderStatus) {
|
private getOrderStatus(status: OrderStatus) {
|
||||||
switch (status) {
|
const statusMap = {
|
||||||
case 'paid':
|
paid: 'succeeded',
|
||||||
return 'succeeded';
|
pending: 'pending',
|
||||||
case 'pending':
|
failed: 'failed',
|
||||||
return 'pending';
|
refunded: 'failed',
|
||||||
case 'failed':
|
} satisfies Record<OrderStatus, UpsertOrderParams['status']>
|
||||||
return 'failed';
|
|
||||||
case 'refunded':
|
return statusMap[status] ?? 'pending';
|
||||||
return 'failed';
|
|
||||||
default:
|
|
||||||
return 'pending';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ export function AccountSelector({
|
|||||||
role="combobox"
|
role="combobox"
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
className={cn(
|
className={cn(
|
||||||
'dark:shadow-primary/10 group w-full min-w-0 px-2 lg:w-auto lg:max-w-fit',
|
'dark:shadow-primary/10 group w-full min-w-0 px-2 lg:w-auto lg:max-w-fit mr-1',
|
||||||
{
|
{
|
||||||
'justify-start': !collapsed,
|
'justify-start': !collapsed,
|
||||||
'm-auto justify-center px-2 lg:w-full': collapsed,
|
'm-auto justify-center px-2 lg:w-full': collapsed,
|
||||||
@@ -114,7 +114,7 @@ export function AccountSelector({
|
|||||||
<span
|
<span
|
||||||
className={cn('flex max-w-full items-center', {
|
className={cn('flex max-w-full items-center', {
|
||||||
'justify-center gap-x-0': collapsed,
|
'justify-center gap-x-0': collapsed,
|
||||||
'gap-x-4': !collapsed,
|
'gap-x-2': !collapsed,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<PersonalAccountAvatar />
|
<PersonalAccountAvatar />
|
||||||
@@ -133,7 +133,7 @@ export function AccountSelector({
|
|||||||
<span
|
<span
|
||||||
className={cn('flex max-w-full items-center', {
|
className={cn('flex max-w-full items-center', {
|
||||||
'justify-center gap-x-0': collapsed,
|
'justify-center gap-x-0': collapsed,
|
||||||
'gap-x-4': !collapsed,
|
'gap-x-2': !collapsed,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Avatar className={'rounded-xs h-6 w-6'}>
|
<Avatar className={'rounded-xs h-6 w-6'}>
|
||||||
@@ -158,7 +158,7 @@ export function AccountSelector({
|
|||||||
</If>
|
</If>
|
||||||
|
|
||||||
<CaretSortIcon
|
<CaretSortIcon
|
||||||
className={cn('ml-2 h-4 w-4 shrink-0 opacity-50', {
|
className={cn('ml-1 h-4 w-4 shrink-0 opacity-50', {
|
||||||
hidden: collapsed,
|
hidden: collapsed,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export function AccountMembersTable({
|
|||||||
})
|
})
|
||||||
.sort((prev, next) => {
|
.sort((prev, next) => {
|
||||||
if (prev.primary_owner_user_id === prev.user_id) {
|
if (prev.primary_owner_user_id === prev.user_id) {
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prev.role_hierarchy_level < next.role_hierarchy_level) {
|
if (prev.role_hierarchy_level < next.role_hierarchy_level) {
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export function parseAcceptLanguageHeader(
|
|||||||
const trimmedLocale = locale.trim();
|
const trimmedLocale = locale.trim();
|
||||||
const numQ = Number(q.replace(/q ?=/, ''));
|
const numQ = Number(q.replace(/q ?=/, ''));
|
||||||
|
|
||||||
return [isNaN(numQ) ? 0 : numQ, trimmedLocale];
|
return [Number.isNaN(numQ) ? 0 : numQ, trimmedLocale];
|
||||||
})
|
})
|
||||||
.sort(([q1], [q2]) => q2 - q1) // Sort by quality value in descending order
|
.sort(([q1], [q2]) => q2 - q1) // Sort by quality value in descending order
|
||||||
.flatMap(([_, locale]) => {
|
.flatMap(([_, locale]) => {
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ function onError({
|
|||||||
|
|
||||||
console.error(
|
console.error(
|
||||||
{
|
{
|
||||||
error,
|
error: JSON.stringify(error).replace(/["\\]/g, '\\$&'),
|
||||||
name: `auth.callback`,
|
name: `auth.callback`,
|
||||||
},
|
},
|
||||||
`An error occurred while signing user in`,
|
`An error occurred while signing user in`,
|
||||||
|
|||||||
@@ -392,7 +392,7 @@ const SidebarHeader: React.FC<React.ComponentPropsWithRef<'div'>> = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-sidebar="header"
|
data-sidebar="header"
|
||||||
className={cn('flex flex-col gap-2 p-2', className)}
|
className={cn('flex flex-col gap-2 p-2 group-data-[state=collapsed]:group-data-[collapsible=offcanvas]:hidden', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const whitelist = {
|
|||||||
STRIPE_WEBHOOK_SECRET: [/whsec_*/],
|
STRIPE_WEBHOOK_SECRET: [/whsec_*/],
|
||||||
EMAIL_PASSWORD: ['password'],
|
EMAIL_PASSWORD: ['password'],
|
||||||
SUPABASE_DB_WEBHOOK_SECRET: ['WEBHOOKSECRET'],
|
SUPABASE_DB_WEBHOOK_SECRET: ['WEBHOOKSECRET'],
|
||||||
SUPABASE_SERVICE_ROLE_KEY: ['eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU'],
|
SUPABASE_SERVICE_ROLE_KEY: [/qQwv8Hdp7fsn3W0YpN81IU/],
|
||||||
};
|
};
|
||||||
|
|
||||||
// List of sensitive environment variables that should not be in .env files
|
// List of sensitive environment variables that should not be in .env files
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ async function checkLicense() {
|
|||||||
try {
|
try {
|
||||||
const makerkitConfig =
|
const makerkitConfig =
|
||||||
JSON.parse(
|
JSON.parse(
|
||||||
readFileSync(path.resolve(process.cwd(), '../../.makerkitrc'), 'utf-8'),
|
readFileSync(path.resolve(process.cwd(), '../../.makerkitrc'), 'utf8'),
|
||||||
) || {};
|
) || {};
|
||||||
|
|
||||||
if (makerkitConfig.projectName) {
|
if (makerkitConfig.projectName) {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export namespace generator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function loadEnvironmentVariables(filePath: string) {
|
export function loadEnvironmentVariables(filePath: string) {
|
||||||
const file = readFileSync(filePath, 'utf-8');
|
const file = readFileSync(filePath, 'utf8');
|
||||||
const vars = file.split('\n').filter((line) => line.trim() !== '');
|
const vars = file.split('\n').filter((line) => line.trim() !== '');
|
||||||
|
|
||||||
const env: Record<string, string> = {};
|
const env: Record<string, string> = {};
|
||||||
|
|||||||
Reference in New Issue
Block a user