Skip to content

Added entityName prop for custom labels #700

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"@vitejs/plugin-react": "^4.3.3",
"chokidar-cli": "^3.0.0",
"concurrently": "^8.2.2",
"esbuild": "^0.24.0",
"eslint": "8.30.0",
"eslint-config-next": "^14.2.17",
"eslint-plugin-import": "^2.31.0",
Expand All @@ -77,14 +78,14 @@
"eslint-plugin-react": "^7.37.2",
"jsdom": "^24.1.3",
"only-allow": "^1.2.1",
"pluralize": "^8.0.0",
"rimraf": "^5.0.10",
"tsup": "^8.3.5",
"turbo": "^2.2.3",
"typescript": "5.3.3",
"vitest": "^1.6.0",
"vite-tsconfig-paths": "^4.3.2",
"wait-on": "^8.0.1",
"esbuild": "^0.24.0"
"vitest": "^1.6.0",
"wait-on": "^8.0.1"
},
"pnpm": {
"overrides": {
Expand Down
1 change: 1 addition & 0 deletions packages/template/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"jose": "^5.2.2",
"js-cookie": "^3.0.5",
"lucide-react": "^0.378.0",
"pluralize": "^8.0.0",
"oauth4webapi": "^2.10.3",
"@oslojs/otp": "^1.1.0",
"qrcode": "^1.5.4",
Expand Down
11 changes: 7 additions & 4 deletions packages/template/src/components-page/account-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ProfilePage } from "./account-settings/profile-page/profile-page";
import { SettingsPage } from './account-settings/settings/settings-page';
import { TeamCreationPage } from './account-settings/teams/team-creation-page';
import { TeamPage } from './account-settings/teams/team-page';
import { pluralize } from "pluralize";

const Icon = ({ name }: { name: keyof typeof icons }) => {
const LucideIcon = icons[name];
Expand All @@ -23,6 +24,7 @@ const Icon = ({ name }: { name: keyof typeof icons }) => {

export function AccountSettings(props: {
fullPage?: boolean,
entityName?: string
extraItems?: ({
title: string,
content: React.ReactNode,
Expand All @@ -38,6 +40,7 @@ export function AccountSettings(props: {
const teams = user.useTeams();
const stackApp = useStackApp();
const project = stackApp.useProject();
const entityName = props.entityName ?? "Team";

return (
<MaybeFullPage fullPage={!!props.fullPage}>
Expand Down Expand Up @@ -101,7 +104,7 @@ export function AccountSettings(props: {
content: item.content,
} as const)) || []),
...(teams.length > 0 || project.config.clientTeamCreationEnabled) ? [{
title: t('Teams'),
title: t(`${pluralize(entityName)}`),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: t() call with template literal could cause translation issues. Consider using t('plural_entity', {entity: entityName})

Suggested change
title: t(`${pluralize(entityName)}`),
title: t('plural_entity', { entity: entityName }),

type: 'divider',
}] as const : [],
...teams.map(team => ({
Expand All @@ -110,16 +113,16 @@ export function AccountSettings(props: {
<Typography className="max-w-[320px] md:w-[90%] truncate">{team.displayName}</Typography>
</div>,
type: 'item',
id: `team-${team.id}`,
id: `${entityName.toLowerCase()}-${team.id}`,
content: <Suspense fallback={<TeamPageSkeleton/>}>
<TeamPage team={team}/>
</Suspense>,
} as const)),
...project.config.clientTeamCreationEnabled ? [{
title: t('Create a team'),
title: t(`Create a ${entityName.toLowerCase()}`),
icon: <Icon name="CirclePlus"/>,
type: 'item',
id: 'team-creation',
id: `team-creation`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: ID still uses hardcoded 'team-creation' instead of entityName like other IDs

content: <Suspense fallback={<TeamCreationSkeleton/>}>
<TeamCreationPage />
</Suspense>,
Expand Down
12 changes: 8 additions & 4 deletions packages/template/src/components-page/team-creation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ import { FormWarningText } from "../components/elements/form-warning";
import { MaybeFullPage } from "../components/elements/maybe-full-page";
import { useTranslation } from "../lib/translations";

export function TeamCreation(props: { fullPage?: boolean }) {
export function TeamCreation(props: {
fullPage?: boolean,
entityName?: string,
}) {
const { t } = useTranslation();
const entityName = props.entityName ? props.entityName : "Team";

const schema = yupObject({
displayName: yupString().defined().nonEmpty(t('Please enter a team name')),
displayName: yupString().defined().nonEmpty(t(`Please enter ${entityName.toLowerCase()} name`)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Translation string with interpolation should use a translation key instead of direct interpolation to support proper localization

});

const { register, handleSubmit, formState: { errors } } = useForm({
Expand All @@ -29,7 +33,7 @@ export function TeamCreation(props: { fullPage?: boolean }) {
const navigate = app.useNavigate();

if (!project.config.clientTeamCreationEnabled) {
return <MessageCard title={t('Team creation is not enabled')} />;
return <MessageCard title={t(`${entityName} creation is not enabled`)} />;
}

const onSubmit = async (data: yup.InferType<typeof schema>) => {
Expand All @@ -48,7 +52,7 @@ export function TeamCreation(props: { fullPage?: boolean }) {
<div className='stack-scope flex flex-col items-stretch' style={{ maxWidth: '380px', flexBasis: '380px', padding: props.fullPage ? '1rem' : 0 }}>
<div className="text-center mb-6">
<Typography type='h2'>
{t('Create a Team')}
{t(`Create ${entityName}`)}
</Typography>
</div>
<form
Expand Down
15 changes: 9 additions & 6 deletions packages/template/src/components/selected-team-switcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ import { Suspense, useEffect, useMemo } from "react";
import { Team, useStackApp, useUser } from "..";
import { useTranslation } from "../lib/translations";
import { TeamIcon } from "./team-icon";
import { pluralize } from 'pluralize';

type SelectedTeamSwitcherProps = {
urlMap?: (team: Team) => string,
selectedTeam?: Team,
noUpdateSelectedTeam?: boolean,
entityName?: string,
};

export function SelectedTeamSwitcher(props: SelectedTeamSwitcherProps) {
Expand All @@ -44,6 +46,7 @@ function Inner(props: SelectedTeamSwitcherProps) {
const selectedTeam = user?.selectedTeam || props.selectedTeam;
const rawTeams = user?.useTeams();
const teams = useMemo(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);
const entityName = props.entityName ? props.entityName : "Team";

useEffect(() => {
if (!props.noUpdateSelectedTeam && props.selectedTeam) {
Expand All @@ -58,7 +61,7 @@ function Inner(props: SelectedTeamSwitcherProps) {
runAsynchronouslyWithAlert(async () => {
const team = teams?.find(team => team.id === value);
if (!team) {
throw new Error('Team not found, this should not happen');
throw new Error(`${entityName} not found, this should not happen`);
}

if (!props.noUpdateSelectedTeam) {
Expand All @@ -71,14 +74,14 @@ function Inner(props: SelectedTeamSwitcherProps) {
}}
>
<SelectTrigger className="stack-scope max-w-64">
<SelectValue placeholder="Select team"/>
<SelectValue placeholder={`Select ${entityName.toLowerCase()}`}/>
</SelectTrigger>
<SelectContent className="stack-scope">
{user?.selectedTeam ? <SelectGroup>
<SelectLabel>
<div className="flex items-center justify-between">
<span>
{t('Current team')}
{t(`Current ${entityName.toLowerCase()}`)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Translation key uses string interpolation which may not work with all translation systems. Consider using a mapping of fixed keys instead.

</span>
<Button variant='ghost' size='icon' className="h-6 w-6" onClick={() => navigate(`${app.urls.accountSettings}#team-${user.selectedTeam?.id}`)}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: URL path still uses hardcoded 'team-' prefix in #team-${user.selectedTeam?.id}. Should use entityName for consistency.

<Settings className="h-4 w-4"/>
Expand All @@ -95,7 +98,7 @@ function Inner(props: SelectedTeamSwitcherProps) {

{teams?.length ?
<SelectGroup>
<SelectLabel>{t('Other teams')}</SelectLabel>
<SelectLabel>{t(`Other ${pluralize(entityName.toLowerCase())}`)}</SelectLabel>
{teams.filter(team => team.id !== user?.selectedTeam?.id)
.map(team => (
<SelectItem value={team.id} key={team.id}>
Expand All @@ -107,7 +110,7 @@ function Inner(props: SelectedTeamSwitcherProps) {
))}
</SelectGroup> :
<SelectGroup>
<SelectLabel>{t('No teams yet')}</SelectLabel>
<SelectLabel>{t(`No ${pluralize(entityName.toLowerCase())} yet`)}</SelectLabel>
</SelectGroup>}

{project.config.clientTeamCreationEnabled && <>
Expand All @@ -118,7 +121,7 @@ function Inner(props: SelectedTeamSwitcherProps) {
className="w-full"
variant='ghost'
>
<PlusCircle className="mr-2 h-4 w-4"/> {t('Create a team')}
<PlusCircle className="mr-2 h-4 w-4"/> {t(`Create ${entityName.toLowerCase()}`)}
</Button>
</div>
</>}
Expand Down