forked from baron/baron-sso
개발자 권한을 페이지별로 선택/부여 가능하도록 개선
This commit is contained in:
@@ -36,6 +36,12 @@ import {
|
||||
import { t } from "../../lib/i18n";
|
||||
import { resolveProfileRole } from "../../lib/role";
|
||||
import { fetchMe } from "../auth/authApi";
|
||||
import {
|
||||
developerAccessPageOptions,
|
||||
normalizeDeveloperAccessPages,
|
||||
normalizeDeveloperAccessPageSelection,
|
||||
type DeveloperAccessPage,
|
||||
} from "../developer-access/developerAccessPages";
|
||||
|
||||
function formatUserLabel(user: DevAssignableUser) {
|
||||
const primary = user.name.trim() || user.email.trim();
|
||||
@@ -62,6 +68,9 @@ export default function DeveloperGrantsPage() {
|
||||
const [selectedUser, setSelectedUser] = useState<DevAssignableUser | null>(
|
||||
null,
|
||||
);
|
||||
const [selectedAccessPages, setSelectedAccessPages] = useState<
|
||||
DeveloperAccessPage[]
|
||||
>(["all"]);
|
||||
const [grantNotes, setGrantNotes] = useState("");
|
||||
const [adminNotes, setAdminNotes] = useState<Record<number, string>>({});
|
||||
|
||||
@@ -122,6 +131,7 @@ export default function DeveloperGrantsPage() {
|
||||
);
|
||||
setSelectedUser(null);
|
||||
setUserSearch("");
|
||||
setSelectedAccessPages(["all"]);
|
||||
setGrantNotes("");
|
||||
},
|
||||
onError: (err: AxiosError<{ error?: string }> | Error) => {
|
||||
@@ -212,12 +222,28 @@ export default function DeveloperGrantsPage() {
|
||||
tenantId,
|
||||
reason: grantNotes.trim() || "직접 부여",
|
||||
adminNotes: grantNotes.trim(),
|
||||
accessPages: normalizeDeveloperAccessPageSelection(selectedAccessPages),
|
||||
});
|
||||
};
|
||||
|
||||
const handleSelectUser = (user: DevAssignableUser) => {
|
||||
setSelectedUser(user);
|
||||
setUserSearch(formatUserLabel(user));
|
||||
setSelectedAccessPages(["all"]);
|
||||
};
|
||||
|
||||
const handleAccessPageToggle = (page: DeveloperAccessPage) => {
|
||||
setSelectedAccessPages((current) => {
|
||||
if (page === "all") {
|
||||
return ["all"];
|
||||
}
|
||||
const withoutAll = current.filter((item) => item !== "all");
|
||||
if (withoutAll.includes(page)) {
|
||||
const next = withoutAll.filter((item) => item !== page);
|
||||
return next.length > 0 ? next : ["all"];
|
||||
}
|
||||
return normalizeDeveloperAccessPageSelection([...withoutAll, page]);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -430,6 +456,40 @@ export default function DeveloperGrantsPage() {
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>
|
||||
{t("ui.dev.grants.pages", "권한 페이지")}{" "}
|
||||
<span className="text-destructive">*</span>
|
||||
</Label>
|
||||
<div className="grid gap-2 rounded-lg border border-border/60 bg-muted/20 p-3">
|
||||
{developerAccessPageOptions.map((option) => {
|
||||
const checked =
|
||||
option.value === "all"
|
||||
? selectedAccessPages.includes("all")
|
||||
: selectedAccessPages.includes(option.value);
|
||||
return (
|
||||
<label
|
||||
key={option.value}
|
||||
className="flex items-center gap-3 rounded-md px-2 py-1.5 text-sm hover:bg-background/60"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checked}
|
||||
onChange={() => handleAccessPageToggle(option.value)}
|
||||
/>
|
||||
<span className="font-medium">{option.label}</span>
|
||||
</label>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t(
|
||||
"msg.dev.grants.pages_hint",
|
||||
"전체를 선택하면 개요, 연동 앱 추가, 감사로그가 모두 포함됩니다.",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
@@ -518,6 +578,7 @@ export default function DeveloperGrantsPage() {
|
||||
<TableHead>{t("ui.dev.grants.user", "사용자")}</TableHead>
|
||||
<TableHead>{t("ui.dev.grants.tenant", "테넌트")}</TableHead>
|
||||
<TableHead>{t("ui.dev.grants.reason", "부여 사유")}</TableHead>
|
||||
<TableHead>{t("ui.dev.grants.pages", "권한 페이지")}</TableHead>
|
||||
<TableHead>{t("ui.dev.grants.status", "상태")}</TableHead>
|
||||
<TableHead>{t("ui.dev.grants.date", "부여일")}</TableHead>
|
||||
<TableHead className="text-right">
|
||||
@@ -536,7 +597,7 @@ export default function DeveloperGrantsPage() {
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{grant.email || grant.userId}
|
||||
</div>
|
||||
<div className="font-mono text-xs text-muted-foreground">
|
||||
<div className="font-mono text-xs text-muted-foreground">
|
||||
{grant.userId}
|
||||
</div>
|
||||
</div>
|
||||
@@ -561,6 +622,20 @@ export default function DeveloperGrantsPage() {
|
||||
</div>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{(grant.accessPages?.length
|
||||
? normalizeDeveloperAccessPages(grant.accessPages)
|
||||
: ["all"]
|
||||
).map((page) => (
|
||||
<Badge key={page} variant="outline">
|
||||
{developerAccessPageOptions.find(
|
||||
(option) => option.value === page,
|
||||
)?.label ?? page}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="success">
|
||||
{t("ui.dev.grants.approved", "승인됨")}
|
||||
|
||||
Reference in New Issue
Block a user