@nexttylabs/echo 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/app/(dashboard)/dashboard/page.tsx +48 -20
- package/app/(dashboard)/settings/organization/page.tsx +11 -5
- package/components/settings/invite-member-form.tsx +11 -8
- package/components/settings/organization-members-list.tsx +15 -12
- package/messages/en.json +32 -0
- package/messages/jp.json +32 -0
- package/messages/zh-CN.json +32 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @nexttylabs/echo
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- fc3d8f1: support light/dark theme
|
|
8
|
+
- cc04d9f: Use the organization member role uniformly.
|
|
9
|
+
- 5cfdeb7: feat: support editing user profile
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 76fda4d: fix organizational setting i18n
|
|
14
|
+
- 7dba561: remove legacy changeset files
|
|
15
|
+
- 158b254: show current orgnization
|
|
16
|
+
- 1e798f1: remove unuse tables and published files
|
|
17
|
+
- e3c4e1c: fix workflow errors
|
|
18
|
+
- daf9abb: first release
|
|
19
|
+
|
|
20
|
+
## 0.8.0
|
|
21
|
+
|
|
22
|
+
### Minor Changes
|
|
23
|
+
|
|
24
|
+
- fc3d8f1: support light/dark theme
|
|
25
|
+
- cc04d9f: Use the organization member role uniformly.
|
|
26
|
+
- 5cfdeb7: feat: support editing user profile
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- 7dba561: remove legacy changeset files
|
|
31
|
+
- 158b254: show current orgnization
|
|
32
|
+
- 1e798f1: remove unuse tables and published files
|
|
33
|
+
- e3c4e1c: fix workflow errors
|
|
34
|
+
- daf9abb: first release
|
|
35
|
+
|
|
3
36
|
## 0.7.0
|
|
4
37
|
|
|
5
38
|
### Minor Changes
|
|
@@ -18,14 +18,16 @@
|
|
|
18
18
|
import { cookies, headers } from "next/headers";
|
|
19
19
|
import { getTranslations } from "next-intl/server";
|
|
20
20
|
import Link from "next/link";
|
|
21
|
-
import { ExternalLink } from "lucide-react";
|
|
21
|
+
import { ExternalLink, Building2 } from "lucide-react";
|
|
22
22
|
import { auth } from "@/lib/auth/config";
|
|
23
23
|
import { db } from "@/lib/db";
|
|
24
24
|
import { getDashboardStats, type DashboardStats } from "@/lib/dashboard/get-dashboard-stats";
|
|
25
|
-
import { getUserOrganizations } from "@/lib/auth/organization";
|
|
25
|
+
import { getUserOrganizations, type UserOrganization } from "@/lib/auth/organization";
|
|
26
26
|
import { getOrgContext } from "@/lib/auth/org-context";
|
|
27
27
|
import { StatsCards, RecentFeedbackList, StatusChart } from "@/components/dashboard";
|
|
28
28
|
import { Button } from "@/components/ui/button";
|
|
29
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
30
|
+
import { Badge } from "@/components/ui/badge";
|
|
29
31
|
import type { UserRole } from "@/lib/auth/permissions";
|
|
30
32
|
|
|
31
33
|
interface PageProps {
|
|
@@ -49,8 +51,8 @@ export default async function DashboardPage({ searchParams }: PageProps) {
|
|
|
49
51
|
recentFeedback: [],
|
|
50
52
|
};
|
|
51
53
|
|
|
52
|
-
let
|
|
53
|
-
let organizations:
|
|
54
|
+
let currentOrganization: UserOrganization | null = null;
|
|
55
|
+
let organizations: UserOrganization[] = [];
|
|
54
56
|
let shouldPersistDefaultOrg = false;
|
|
55
57
|
|
|
56
58
|
if (db && session?.user) {
|
|
@@ -81,7 +83,7 @@ export default async function DashboardPage({ searchParams }: PageProps) {
|
|
|
81
83
|
requireMembership: true,
|
|
82
84
|
});
|
|
83
85
|
|
|
84
|
-
|
|
86
|
+
currentOrganization = organizations.find((org) => org.id === context.organizationId) ?? null;
|
|
85
87
|
|
|
86
88
|
stats = await getDashboardStats(db, {
|
|
87
89
|
userId: session.user.id,
|
|
@@ -96,23 +98,49 @@ export default async function DashboardPage({ searchParams }: PageProps) {
|
|
|
96
98
|
|
|
97
99
|
return (
|
|
98
100
|
<div className="p-6 space-y-6">
|
|
99
|
-
<div
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
<p className="text-muted-foreground">{t("pageDescription")}</p>
|
|
103
|
-
</div>
|
|
104
|
-
<div className="flex items-center gap-3">
|
|
105
|
-
{organizationSlug && (
|
|
106
|
-
<Button asChild variant="outline">
|
|
107
|
-
<Link href={`/${organizationSlug}`} target="_blank">
|
|
108
|
-
<ExternalLink className="mr-2 h-4 w-4" />
|
|
109
|
-
{t("visitPortal")}
|
|
110
|
-
</Link>
|
|
111
|
-
</Button>
|
|
112
|
-
)}
|
|
113
|
-
</div>
|
|
101
|
+
<div>
|
|
102
|
+
<h1 className="text-2xl font-bold">{t("pageTitle")}</h1>
|
|
103
|
+
<p className="text-muted-foreground">{t("pageDescription")}</p>
|
|
114
104
|
</div>
|
|
115
105
|
|
|
106
|
+
{/* Current Organization Info Card */}
|
|
107
|
+
{currentOrganization && (
|
|
108
|
+
<Card>
|
|
109
|
+
<CardHeader className="pb-3">
|
|
110
|
+
<CardTitle className="flex items-center gap-2 text-lg">
|
|
111
|
+
<Building2 className="h-5 w-5" />
|
|
112
|
+
{t("currentOrganization")}
|
|
113
|
+
</CardTitle>
|
|
114
|
+
</CardHeader>
|
|
115
|
+
<CardContent>
|
|
116
|
+
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
117
|
+
<div className="space-y-1">
|
|
118
|
+
<div className="flex items-center gap-2">
|
|
119
|
+
<span className="font-semibold">{currentOrganization.name}</span>
|
|
120
|
+
<Badge variant="secondary">{currentOrganization.role}</Badge>
|
|
121
|
+
</div>
|
|
122
|
+
{currentOrganization.slug && (
|
|
123
|
+
<p className="text-sm text-muted-foreground">
|
|
124
|
+
{t("slug")}: <code className="rounded bg-muted px-1 py-0.5">{currentOrganization.slug}</code>
|
|
125
|
+
</p>
|
|
126
|
+
)}
|
|
127
|
+
{currentOrganization.description && (
|
|
128
|
+
<p className="text-sm text-muted-foreground">{currentOrganization.description}</p>
|
|
129
|
+
)}
|
|
130
|
+
</div>
|
|
131
|
+
{currentOrganization.slug && (
|
|
132
|
+
<Button asChild variant="outline" size="sm">
|
|
133
|
+
<Link href={`/${currentOrganization.slug}`} target="_blank">
|
|
134
|
+
<ExternalLink className="mr-2 h-4 w-4" />
|
|
135
|
+
{t("visitPortal")}
|
|
136
|
+
</Link>
|
|
137
|
+
</Button>
|
|
138
|
+
)}
|
|
139
|
+
</div>
|
|
140
|
+
</CardContent>
|
|
141
|
+
</Card>
|
|
142
|
+
)}
|
|
143
|
+
|
|
116
144
|
<StatsCards
|
|
117
145
|
totalFeedback={stats.totalFeedback}
|
|
118
146
|
pendingFeedback={stats.pendingFeedback}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { cookies, headers } from "next/headers";
|
|
19
19
|
import { redirect } from "next/navigation";
|
|
20
|
+
import { getTranslations } from "next-intl/server";
|
|
20
21
|
import { auth } from "@/lib/auth/config";
|
|
21
22
|
import { db } from "@/lib/db";
|
|
22
23
|
import { getUserOrganizations } from "@/lib/auth/organization";
|
|
@@ -27,11 +28,15 @@ import type { UserRole } from "@/lib/auth/permissions";
|
|
|
27
28
|
import { organizationMembers, user } from "@/lib/db/schema";
|
|
28
29
|
import { eq } from "drizzle-orm";
|
|
29
30
|
|
|
30
|
-
export
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
export async function generateMetadata() {
|
|
32
|
+
const t = await getTranslations("settings.organizationPage");
|
|
33
|
+
return {
|
|
34
|
+
title: `${t("pageTitle")} - Echo`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
33
37
|
|
|
34
38
|
export default async function OrganizationSettingsPage() {
|
|
39
|
+
const t = await getTranslations("settings.organizationPage");
|
|
35
40
|
const session = await auth.api.getSession({ headers: await headers() });
|
|
36
41
|
|
|
37
42
|
if (!session?.user) {
|
|
@@ -80,8 +85,8 @@ export default async function OrganizationSettingsPage() {
|
|
|
80
85
|
return (
|
|
81
86
|
<div className="max-w-4xl space-y-6">
|
|
82
87
|
<div>
|
|
83
|
-
<h1 className="text-2xl font-semibold"
|
|
84
|
-
<p className="text-muted-foreground"
|
|
88
|
+
<h1 className="text-2xl font-semibold">{t("pageTitle")}</h1>
|
|
89
|
+
<p className="text-muted-foreground">{t("pageDescription")}</p>
|
|
85
90
|
</div>
|
|
86
91
|
|
|
87
92
|
<OrganizationForm
|
|
@@ -101,3 +106,4 @@ export default async function OrganizationSettingsPage() {
|
|
|
101
106
|
</div>
|
|
102
107
|
);
|
|
103
108
|
}
|
|
109
|
+
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import { useState } from "react";
|
|
22
|
+
import { useTranslations } from "next-intl";
|
|
22
23
|
import { Button } from "@/components/ui/button";
|
|
23
24
|
import {
|
|
24
25
|
Card,
|
|
@@ -38,6 +39,7 @@ type InviteMemberFormProps = {
|
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
export function InviteMemberForm({ organizationId }: InviteMemberFormProps) {
|
|
42
|
+
const t = useTranslations("settings.organizationPage.invite");
|
|
41
43
|
const [email, setEmail] = useState("");
|
|
42
44
|
const [isLoading, setIsLoading] = useState(false);
|
|
43
45
|
const [error, setError] = useState<string | null>(null);
|
|
@@ -58,12 +60,12 @@ export function InviteMemberForm({ organizationId }: InviteMemberFormProps) {
|
|
|
58
60
|
const json = await res.json().catch(() => null);
|
|
59
61
|
|
|
60
62
|
if (!res.ok) {
|
|
61
|
-
setError(json?.error ?? "
|
|
63
|
+
setError(json?.error ?? t("sendFailed"));
|
|
62
64
|
setIsLoading(false);
|
|
63
65
|
return;
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
setSuccess("
|
|
68
|
+
setSuccess(t("sendSuccess"));
|
|
67
69
|
setEmail("");
|
|
68
70
|
setIsLoading(false);
|
|
69
71
|
};
|
|
@@ -71,8 +73,8 @@ export function InviteMemberForm({ organizationId }: InviteMemberFormProps) {
|
|
|
71
73
|
return (
|
|
72
74
|
<Card>
|
|
73
75
|
<CardHeader>
|
|
74
|
-
<CardTitle
|
|
75
|
-
<CardDescription
|
|
76
|
+
<CardTitle>{t("title")}</CardTitle>
|
|
77
|
+
<CardDescription>{t("description")}</CardDescription>
|
|
76
78
|
</CardHeader>
|
|
77
79
|
<CardContent>
|
|
78
80
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
@@ -88,7 +90,7 @@ export function InviteMemberForm({ organizationId }: InviteMemberFormProps) {
|
|
|
88
90
|
) : null}
|
|
89
91
|
|
|
90
92
|
<div className="space-y-2">
|
|
91
|
-
<Label htmlFor="invite-email"
|
|
93
|
+
<Label htmlFor="invite-email">{t("emailLabel")}</Label>
|
|
92
94
|
<Input
|
|
93
95
|
id="invite-email"
|
|
94
96
|
name="email"
|
|
@@ -102,18 +104,19 @@ export function InviteMemberForm({ organizationId }: InviteMemberFormProps) {
|
|
|
102
104
|
</div>
|
|
103
105
|
|
|
104
106
|
<div className="space-y-2">
|
|
105
|
-
<Label
|
|
107
|
+
<Label>{t("roleLabel")}</Label>
|
|
106
108
|
<div className="flex items-center gap-3 rounded-md border border-dashed border-slate-200 px-3 py-2 text-sm text-slate-600">
|
|
107
109
|
<Badge variant="secondary">{DEFAULT_ROLE}</Badge>
|
|
108
|
-
<span
|
|
110
|
+
<span>{t("roleNote")}</span>
|
|
109
111
|
</div>
|
|
110
112
|
</div>
|
|
111
113
|
|
|
112
114
|
<Button type="submit" className="w-full" disabled={isLoading || !email}>
|
|
113
|
-
{isLoading ? "
|
|
115
|
+
{isLoading ? t("sending") : t("send")}
|
|
114
116
|
</Button>
|
|
115
117
|
</form>
|
|
116
118
|
</CardContent>
|
|
117
119
|
</Card>
|
|
118
120
|
);
|
|
119
121
|
}
|
|
122
|
+
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import { useState } from "react";
|
|
22
|
+
import { useTranslations } from "next-intl";
|
|
22
23
|
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog";
|
|
23
24
|
import { Button } from "@/components/ui/button";
|
|
24
25
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
@@ -47,6 +48,7 @@ export function OrganizationMembersList({
|
|
|
47
48
|
currentUserId,
|
|
48
49
|
initialMembers,
|
|
49
50
|
}: OrganizationMembersListProps) {
|
|
51
|
+
const t = useTranslations("settings.organizationPage.members");
|
|
50
52
|
const [members, setMembers] = useState(initialMembers);
|
|
51
53
|
const [error, setError] = useState<string | null>(null);
|
|
52
54
|
const [pendingMemberId, setPendingMemberId] = useState<string | null>(null);
|
|
@@ -68,7 +70,7 @@ export function OrganizationMembersList({
|
|
|
68
70
|
|
|
69
71
|
const json = await res.json().catch(() => null);
|
|
70
72
|
if (!res.ok) {
|
|
71
|
-
setError(json?.error ?? "
|
|
73
|
+
setError(json?.error ?? t("removeFailed"));
|
|
72
74
|
setPendingMemberId(null);
|
|
73
75
|
return;
|
|
74
76
|
}
|
|
@@ -93,7 +95,7 @@ export function OrganizationMembersList({
|
|
|
93
95
|
|
|
94
96
|
const json = await res.json().catch(() => null);
|
|
95
97
|
if (!res.ok) {
|
|
96
|
-
setError(json?.error ?? "
|
|
98
|
+
setError(json?.error ?? t("updateRoleFailed"));
|
|
97
99
|
setPendingMemberId(null);
|
|
98
100
|
return;
|
|
99
101
|
}
|
|
@@ -110,8 +112,8 @@ export function OrganizationMembersList({
|
|
|
110
112
|
<>
|
|
111
113
|
<Card>
|
|
112
114
|
<CardHeader>
|
|
113
|
-
<CardTitle
|
|
114
|
-
<CardDescription
|
|
115
|
+
<CardTitle>{t("title")}</CardTitle>
|
|
116
|
+
<CardDescription>{t("description")}</CardDescription>
|
|
115
117
|
</CardHeader>
|
|
116
118
|
<CardContent className="space-y-4">
|
|
117
119
|
{error ? (
|
|
@@ -122,14 +124,14 @@ export function OrganizationMembersList({
|
|
|
122
124
|
|
|
123
125
|
{members.length === 0 ? (
|
|
124
126
|
<div className="rounded-md border border-dashed border-slate-200 bg-white/60 px-4 py-6 text-sm text-slate-500">
|
|
125
|
-
|
|
127
|
+
{t("noMembers")}
|
|
126
128
|
</div>
|
|
127
129
|
) : (
|
|
128
130
|
<div className="space-y-3">
|
|
129
131
|
{members.map((member) => {
|
|
130
132
|
const isSelf = currentUserId === member.userId;
|
|
131
133
|
const isPending = pendingMemberId === member.userId;
|
|
132
|
-
const displayName = member.name ?? member.email ?? "
|
|
134
|
+
const displayName = member.name ?? member.email ?? t("unknownMember");
|
|
133
135
|
const secondaryText = member.email ?? member.userId;
|
|
134
136
|
|
|
135
137
|
return (
|
|
@@ -151,7 +153,7 @@ export function OrganizationMembersList({
|
|
|
151
153
|
|
|
152
154
|
{isSelf ? (
|
|
153
155
|
<Button variant="outline" size="sm" disabled>
|
|
154
|
-
|
|
156
|
+
{t("cannotRemoveSelf")}
|
|
155
157
|
</Button>
|
|
156
158
|
) : (
|
|
157
159
|
<Button
|
|
@@ -160,7 +162,7 @@ export function OrganizationMembersList({
|
|
|
160
162
|
disabled={!currentUserId || isPending}
|
|
161
163
|
onClick={() => setMemberToRemove({ id: member.userId, name: displayName })}
|
|
162
164
|
>
|
|
163
|
-
{isPending ? "
|
|
165
|
+
{isPending ? t("removing") : t("remove")}
|
|
164
166
|
</Button>
|
|
165
167
|
)}
|
|
166
168
|
</div>
|
|
@@ -175,19 +177,19 @@ export function OrganizationMembersList({
|
|
|
175
177
|
<AlertDialog open={!!memberToRemove} onOpenChange={(open) => !open && setMemberToRemove(null)}>
|
|
176
178
|
<AlertDialogContent>
|
|
177
179
|
<AlertDialogHeader>
|
|
178
|
-
<AlertDialogTitle
|
|
180
|
+
<AlertDialogTitle>{t("confirmRemoveTitle")}</AlertDialogTitle>
|
|
179
181
|
<AlertDialogDescription>
|
|
180
|
-
{memberToRemove?.name}
|
|
182
|
+
{t("confirmRemoveDescription", { name: memberToRemove?.name ?? "" })}
|
|
181
183
|
</AlertDialogDescription>
|
|
182
184
|
</AlertDialogHeader>
|
|
183
185
|
<AlertDialogFooter>
|
|
184
|
-
<AlertDialogCancel disabled={pendingMemberId !== null}
|
|
186
|
+
<AlertDialogCancel disabled={pendingMemberId !== null}>{t("cancel")}</AlertDialogCancel>
|
|
185
187
|
<AlertDialogAction
|
|
186
188
|
variant="destructive"
|
|
187
189
|
onClick={handleRemove}
|
|
188
190
|
disabled={pendingMemberId !== null}
|
|
189
191
|
>
|
|
190
|
-
{pendingMemberId === memberToRemove?.id ? "
|
|
192
|
+
{pendingMemberId === memberToRemove?.id ? t("removing") : t("confirmRemove")}
|
|
191
193
|
</AlertDialogAction>
|
|
192
194
|
</AlertDialogFooter>
|
|
193
195
|
</AlertDialogContent>
|
|
@@ -195,3 +197,4 @@ export function OrganizationMembersList({
|
|
|
195
197
|
</>
|
|
196
198
|
);
|
|
197
199
|
}
|
|
200
|
+
|
package/messages/en.json
CHANGED
|
@@ -126,6 +126,36 @@
|
|
|
126
126
|
"updatedSuccess": "Organization updated",
|
|
127
127
|
"genericError": "Something went wrong. Please try again later."
|
|
128
128
|
},
|
|
129
|
+
"organizationPage": {
|
|
130
|
+
"pageTitle": "Organization Management",
|
|
131
|
+
"pageDescription": "Manage your organization information and members",
|
|
132
|
+
"members": {
|
|
133
|
+
"title": "Organization Members",
|
|
134
|
+
"description": "View and manage members in the organization",
|
|
135
|
+
"noMembers": "No members in this organization yet.",
|
|
136
|
+
"unknownMember": "Unknown Member",
|
|
137
|
+
"removeFailed": "Failed to remove member, please try again later",
|
|
138
|
+
"updateRoleFailed": "Failed to update role, please try again later",
|
|
139
|
+
"cannotRemoveSelf": "Cannot remove yourself",
|
|
140
|
+
"removing": "Removing...",
|
|
141
|
+
"remove": "Remove",
|
|
142
|
+
"confirmRemoveTitle": "Confirm member removal?",
|
|
143
|
+
"confirmRemoveDescription": "{name} will lose access to the organization. This action cannot be undone.",
|
|
144
|
+
"cancel": "Cancel",
|
|
145
|
+
"confirmRemove": "Confirm Remove"
|
|
146
|
+
},
|
|
147
|
+
"invite": {
|
|
148
|
+
"title": "Invite Members",
|
|
149
|
+
"description": "Invite new members to join the organization via email",
|
|
150
|
+
"emailLabel": "Member Email",
|
|
151
|
+
"roleLabel": "Invite Role",
|
|
152
|
+
"roleNote": "Currently only member role is supported",
|
|
153
|
+
"sendFailed": "Failed to send invitation, please try again later",
|
|
154
|
+
"sendSuccess": "Invitation sent, waiting for acceptance",
|
|
155
|
+
"sending": "Sending...",
|
|
156
|
+
"send": "Send Invitation"
|
|
157
|
+
}
|
|
158
|
+
},
|
|
129
159
|
"profile": {
|
|
130
160
|
"pageTitle": "Profile",
|
|
131
161
|
"pageDescription": "Manage your account information and preferences",
|
|
@@ -557,6 +587,8 @@
|
|
|
557
587
|
"pageTitle": "Dashboard",
|
|
558
588
|
"pageDescription": "Feedback management overview",
|
|
559
589
|
"visitPortal": "Visit Web Portal",
|
|
590
|
+
"currentOrganization": "Current Organization",
|
|
591
|
+
"slug": "Slug",
|
|
560
592
|
"stats": {
|
|
561
593
|
"totalFeedback": "Total Feedback",
|
|
562
594
|
"pending": "Pending",
|
package/messages/jp.json
CHANGED
|
@@ -126,6 +126,36 @@
|
|
|
126
126
|
"updatedSuccess": "組織情報を更新しました",
|
|
127
127
|
"genericError": "エラーが発生しました。後でもう一度お試しください。"
|
|
128
128
|
},
|
|
129
|
+
"organizationPage": {
|
|
130
|
+
"pageTitle": "組織管理",
|
|
131
|
+
"pageDescription": "組織情報とメンバーを管理します",
|
|
132
|
+
"members": {
|
|
133
|
+
"title": "組織メンバー",
|
|
134
|
+
"description": "組織内のメンバーを表示・管理",
|
|
135
|
+
"noMembers": "現在この組織にはメンバーがいません。",
|
|
136
|
+
"unknownMember": "不明なメンバー",
|
|
137
|
+
"removeFailed": "メンバーの削除に失敗しました。後でもう一度お試しください",
|
|
138
|
+
"updateRoleFailed": "ロールの更新に失敗しました。後でもう一度お試しください",
|
|
139
|
+
"cannotRemoveSelf": "自分自身を削除できません",
|
|
140
|
+
"removing": "削除中...",
|
|
141
|
+
"remove": "削除",
|
|
142
|
+
"confirmRemoveTitle": "メンバーを削除しますか?",
|
|
143
|
+
"confirmRemoveDescription": "{name} は組織へのアクセス権を失います。この操作は取り消せません。",
|
|
144
|
+
"cancel": "キャンセル",
|
|
145
|
+
"confirmRemove": "削除を確認"
|
|
146
|
+
},
|
|
147
|
+
"invite": {
|
|
148
|
+
"title": "メンバーを招待",
|
|
149
|
+
"description": "メールでメンバーを組織に招待",
|
|
150
|
+
"emailLabel": "メンバーのメールアドレス",
|
|
151
|
+
"roleLabel": "招待ロール",
|
|
152
|
+
"roleNote": "現在はメンバーロールのみサポートされています",
|
|
153
|
+
"sendFailed": "招待の送信に失敗しました。後でもう一度お試しください",
|
|
154
|
+
"sendSuccess": "招待を送信しました。相手の承認をお待ちください",
|
|
155
|
+
"sending": "送信中...",
|
|
156
|
+
"send": "招待を送信"
|
|
157
|
+
}
|
|
158
|
+
},
|
|
129
159
|
"profile": {
|
|
130
160
|
"pageTitle": "プロフィール",
|
|
131
161
|
"pageDescription": "アカウント情報と設定を管理",
|
|
@@ -557,6 +587,8 @@
|
|
|
557
587
|
"pageTitle": "ダッシュボード",
|
|
558
588
|
"pageDescription": "フィードバック管理の概要",
|
|
559
589
|
"visitPortal": "Web Portalにアクセス",
|
|
590
|
+
"currentOrganization": "現在の組織",
|
|
591
|
+
"slug": "スラッグ",
|
|
560
592
|
"stats": {
|
|
561
593
|
"totalFeedback": "総フィードバック数",
|
|
562
594
|
"pending": "保留中",
|
package/messages/zh-CN.json
CHANGED
|
@@ -126,6 +126,36 @@
|
|
|
126
126
|
"updatedSuccess": "组织信息已更新",
|
|
127
127
|
"genericError": "发生错误,请稍后重试"
|
|
128
128
|
},
|
|
129
|
+
"organizationPage": {
|
|
130
|
+
"pageTitle": "组织管理",
|
|
131
|
+
"pageDescription": "管理您的组织信息和成员",
|
|
132
|
+
"members": {
|
|
133
|
+
"title": "组织成员",
|
|
134
|
+
"description": "查看并移除组织中的成员",
|
|
135
|
+
"noMembers": "当前组织暂无成员。",
|
|
136
|
+
"unknownMember": "未知成员",
|
|
137
|
+
"removeFailed": "移除失败,请稍后重试",
|
|
138
|
+
"updateRoleFailed": "更新角色失败,请稍后重试",
|
|
139
|
+
"cannotRemoveSelf": "不能移除自己",
|
|
140
|
+
"removing": "移除中...",
|
|
141
|
+
"remove": "移除",
|
|
142
|
+
"confirmRemoveTitle": "确认移除成员?",
|
|
143
|
+
"confirmRemoveDescription": "{name} 将失去组织访问权限,此操作无法撤销。",
|
|
144
|
+
"cancel": "取消",
|
|
145
|
+
"confirmRemove": "确认移除"
|
|
146
|
+
},
|
|
147
|
+
"invite": {
|
|
148
|
+
"title": "邀请成员",
|
|
149
|
+
"description": "通过邮箱邀请新成员加入组织",
|
|
150
|
+
"emailLabel": "成员邮箱",
|
|
151
|
+
"roleLabel": "邀请角色",
|
|
152
|
+
"roleNote": "当前仅支持成员角色",
|
|
153
|
+
"sendFailed": "发送邀请失败,请稍后重试",
|
|
154
|
+
"sendSuccess": "邀请已发送,等待对方接受",
|
|
155
|
+
"sending": "发送中...",
|
|
156
|
+
"send": "发送邀请"
|
|
157
|
+
}
|
|
158
|
+
},
|
|
129
159
|
"profile": {
|
|
130
160
|
"pageTitle": "个人资料",
|
|
131
161
|
"pageDescription": "管理你的账户信息和偏好设置",
|
|
@@ -557,6 +587,8 @@
|
|
|
557
587
|
"pageTitle": "仪表盘",
|
|
558
588
|
"pageDescription": "反馈管理概览",
|
|
559
589
|
"visitPortal": "访问 Web Portal",
|
|
590
|
+
"currentOrganization": "当前组织",
|
|
591
|
+
"slug": "标识符",
|
|
560
592
|
"stats": {
|
|
561
593
|
"totalFeedback": "总反馈数",
|
|
562
594
|
"pending": "待处理",
|