feat: select users in campaign access explanations
This commit is contained in:
@@ -5,11 +5,15 @@ import {
|
||||
type ReferenceOptionProvider
|
||||
} from "@govoplan/core-webui";
|
||||
import { campaignJobsQueryParams, type CampaignJobsQueryParameters } from "../features/campaigns/utils/jobListQuery";
|
||||
export { fetchResourceAccessExplanation } from "@govoplan/core-webui";
|
||||
export {
|
||||
fetchResourceAccessExplanation,
|
||||
fetchResourceAccessExplanationSubjects
|
||||
} from "@govoplan/core-webui";
|
||||
export type {
|
||||
AccessDecisionProvenanceItem,
|
||||
ResourceAccessExplanationUser as AccessExplanationUser,
|
||||
ResourceAccessExplanationResponse
|
||||
ResourceAccessExplanationResponse,
|
||||
ResourceAccessExplanationSubjectsResponse
|
||||
} from "@govoplan/core-webui";
|
||||
|
||||
export type CampaignListResponse =
|
||||
|
||||
@@ -9,12 +9,14 @@ import {
|
||||
} from "lucide-react";
|
||||
import {
|
||||
fetchResourceAccessExplanation,
|
||||
fetchResourceAccessExplanationSubjects,
|
||||
getCampaignShares,
|
||||
campaignShareTargetProvider,
|
||||
revokeCampaignShare,
|
||||
upsertCampaignShare,
|
||||
type CampaignShare,
|
||||
type ResourceAccessExplanationResponse } from
|
||||
type ResourceAccessExplanationResponse,
|
||||
type ResourceAccessExplanationSubjectsResponse } from
|
||||
"../../../api/campaigns";
|
||||
import {
|
||||
Button,
|
||||
@@ -72,6 +74,8 @@ export default function CampaignAccessCard({
|
||||
const [sharePermission, setSharePermission] = useState<"read" | "write">("read");
|
||||
const [accessExplanationOpen, setAccessExplanationOpen] = useState(false);
|
||||
const [accessExplanation, setAccessExplanation] = useState<ResourceAccessExplanationResponse | null>(null);
|
||||
const [accessExplanationSubjects, setAccessExplanationSubjects] = useState<ResourceAccessExplanationSubjectsResponse | null>(null);
|
||||
const [accessExplanationSubjectId, setAccessExplanationSubjectId] = useState("");
|
||||
const [accessExplanationLoading, setAccessExplanationLoading] = useState(false);
|
||||
const [ownershipTransfers, setOwnershipTransfers] = useState<OwnershipTransfer[]>([]);
|
||||
const [busy, setBusy] = useState(false);
|
||||
@@ -346,13 +350,24 @@ export default function CampaignAccessCard({
|
||||
}
|
||||
|
||||
async function openAccessExplanation() {
|
||||
if (!auth.user?.id) return;
|
||||
const currentUserId = auth.principal?.membership_id || auth.user?.id;
|
||||
if (!currentUserId) return;
|
||||
setAccessExplanationOpen(true);
|
||||
setAccessExplanation(null);
|
||||
setAccessExplanationSubjects(null);
|
||||
setAccessExplanationLoading(true);
|
||||
try {
|
||||
const subjects = await fetchResourceAccessExplanationSubjects(settings, {
|
||||
tenantId: (auth.active_tenant ?? auth.tenant)?.id
|
||||
});
|
||||
const initialSubjectId = subjects.users.some((user) => user.id === currentUserId)
|
||||
? currentUserId
|
||||
: subjects.users[0]?.id;
|
||||
if (!initialSubjectId) throw new Error("No permitted access-explanation subject is available.");
|
||||
setAccessExplanationSubjects(subjects);
|
||||
setAccessExplanationSubjectId(initialSubjectId);
|
||||
setAccessExplanation(await fetchResourceAccessExplanation(settings, {
|
||||
userId: auth.user.id,
|
||||
userId: initialSubjectId,
|
||||
resourceType: "campaign",
|
||||
resourceId: campaign.id,
|
||||
action: "campaigns:campaign:read",
|
||||
@@ -366,6 +381,26 @@ export default function CampaignAccessCard({
|
||||
}
|
||||
}
|
||||
|
||||
async function selectAccessExplanationSubject(userId: string) {
|
||||
if (userId === accessExplanationSubjectId) return;
|
||||
setAccessExplanationLoading(true);
|
||||
try {
|
||||
const explanation = await fetchResourceAccessExplanation(settings, {
|
||||
userId,
|
||||
resourceType: "campaign",
|
||||
resourceId: campaign.id,
|
||||
action: "campaigns:campaign:read",
|
||||
tenantId: (auth.active_tenant ?? auth.tenant)?.id
|
||||
});
|
||||
setAccessExplanation(explanation);
|
||||
setAccessExplanationSubjectId(userId);
|
||||
} catch (err) {
|
||||
onError(err instanceof Error ? err.message : String(err));
|
||||
} finally {
|
||||
setAccessExplanationLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
const columns = useMemo<DataGridColumn<CampaignShare>[]>(() => [
|
||||
{
|
||||
id: "target",
|
||||
@@ -517,8 +552,15 @@ export default function CampaignAccessCard({
|
||||
<FormField label="i18n:govoplan-campaign.access.2f81a22d"><select value={sharePermission} onChange={(event) => setSharePermission(event.target.value as "read" | "write")}><option value="read">i18n:govoplan-campaign.can_view.1b3e4006</option><option value="write">i18n:govoplan-campaign.can_edit_and_operate.77acb15e</option></select></FormField>
|
||||
</Dialog>
|
||||
|
||||
<Dialog open={accessExplanationOpen} className="campaign-access-dialog" title="i18n:govoplan-core.access_explanation.75ee7f62" onClose={() => { if (!accessExplanationLoading) { setAccessExplanationOpen(false); setAccessExplanation(null); } }} footer={<Button onClick={() => { setAccessExplanationOpen(false); setAccessExplanation(null); }} disabled={accessExplanationLoading}>i18n:govoplan-campaign.close.bbfa773e</Button>}>
|
||||
<ResourceAccessExplanation loading={accessExplanationLoading} explanation={accessExplanation} fallbackResourceLabel={campaign.name || campaign.external_id || campaign.id} />
|
||||
<Dialog open={accessExplanationOpen} className="campaign-access-dialog" title="i18n:govoplan-core.access_explanation.75ee7f62" onClose={() => { if (!accessExplanationLoading) { setAccessExplanationOpen(false); setAccessExplanation(null); setAccessExplanationSubjects(null); setAccessExplanationSubjectId(""); } }} footer={<Button onClick={() => { setAccessExplanationOpen(false); setAccessExplanation(null); setAccessExplanationSubjects(null); setAccessExplanationSubjectId(""); }} disabled={accessExplanationLoading}>i18n:govoplan-campaign.close.bbfa773e</Button>}>
|
||||
<ResourceAccessExplanation
|
||||
loading={accessExplanationLoading}
|
||||
explanation={accessExplanation}
|
||||
subjects={accessExplanationSubjects}
|
||||
selectedUserId={accessExplanationSubjectId}
|
||||
onSelectedUserIdChange={(userId) => void selectAccessExplanationSubject(userId)}
|
||||
fallbackResourceLabel={campaign.name || campaign.external_id || campaign.id}
|
||||
/>
|
||||
</Dialog>
|
||||
|
||||
<ConfirmDialog open={Boolean(revokeTarget)} title="i18n:govoplan-campaign.remove_campaign_share.2c2d42eb" message="i18n:govoplan-campaign.remove_this_user_s_or_group_s_explicit_access_to.5ff07672" confirmLabel="i18n:govoplan-campaign.remove_share.69c932e1" tone="danger" busy={busy} onCancel={() => setRevokeTarget(null)} onConfirm={() => void revoke()} />
|
||||
|
||||
Reference in New Issue
Block a user