manage license app launch policies
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-07-10 13:14:23 +09:00
parent 93fc7b7b4e
commit ac0c8eb29d
7 changed files with 620 additions and 97 deletions
+27 -8
View File
@@ -9,7 +9,7 @@ import {
createManagedUser,
loadLicensePlans,
loadOrganizations,
loadUserSessions,
loadUserSessionsPage,
lockUser,
terminateUserSession,
unlockUser,
@@ -32,6 +32,10 @@ const emptyCreateForm = {
export function UserSessionsPage() {
const { toast } = useToast();
const [sessions, setSessions] = useState<UserSession[]>([]);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [keyword, setKeyword] = useState('');
const [filterValues, setFilterValues] = useState<Record<string, string>>({});
const [organizations, setOrganizations] = useState<Organization[]>([]);
const [licensePlans, setLicensePlans] = useState<LicensePlan[]>([]);
const [loading, setLoading] = useState(true);
@@ -45,9 +49,10 @@ export function UserSessionsPage() {
const refresh = () => {
setLoading(true);
loadUserSessions()
.then((nextSessions) => {
setSessions(nextSessions);
loadUserSessionsPage({ page, limit: 6, keyword, state: filterValues.state })
.then((result) => {
setSessions(result.items);
setTotal(result.total);
setFailed(false);
})
.catch(() => {
@@ -71,7 +76,7 @@ export function UserSessionsPage() {
licenseCode: (current.licenseCode || nextLicensePlans[0]?.code || 'EDU') as 'FREE' | 'EDU' | 'PRO'
}));
});
}, []);
}, [page, keyword, filterValues.state]);
useEffect(() => {
if (!menuSession) return;
@@ -89,7 +94,7 @@ export function UserSessionsPage() {
};
}, [menuSession]);
const rows = useMemo(() => sessions.map((session, index) => ({ ...session, no: sessions.length - index })), [sessions]);
const rows = sessions;
const columns = useMemo<Array<DataTableColumn<UserSession>>>(
() => [
@@ -140,9 +145,9 @@ export function UserSessionsPage() {
const filters = useMemo<Array<DataTableFilter<UserSession>>>(
() => [
{ key: 'state', label: '상태', options: [...new Set(sessions.map((row) => row.state))].map((state) => ({ label: state, value: state })), getValue: (row) => row.state }
{ key: 'state', label: '상태', options: ['유효토큰', '세션 없음', '잠김', '만료', '종료'].map((state) => ({ label: state, value: state })), getValue: (row) => row.state }
],
[sessions]
[]
);
const openCreateModal = () => {
@@ -208,6 +213,20 @@ export function UserSessionsPage() {
minWidth={900}
loading={loading}
emptyMessage="표시할 사용자/세션 데이터가 없습니다."
serverPagination={{
page,
total,
onPageChange: setPage,
onSearchChange: (value) => {
setKeyword(value);
setPage(1);
},
filterValues,
onFilterChange: (key, value) => {
setFilterValues((prev) => ({ ...prev, [key]: value }));
setPage(1);
}
}}
/>
{menuSession ? (() => {
const row = rows.find((item) => item.id === menuSession.id);