Make profile state filters multi-value
Address PR review feedback on the profile-state split: SAML sign-in now activates a pending profile, deactivation counts owners against the profile's own organization to close a last-owner bypass, the migration leaves historical activated_at/deactivated_at NULL rather than fabricating timestamps, and pending members are no longer rendered with the deactivated (faded) styling. Drop the single-value state filter in favor of the multi-value states across the profile and signatory surfaces. Remove ProfileFilter.state (only states[] remains) and convert the signatures profileState filter to profileStates. Turn the console people filter, the CLI "user list --state" flag, and the n8n listUsers and getAllSignatures state inputs into multi-select controls, where an empty selection means all states. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -1371,7 +1371,7 @@
|
|||||||
"columns": { "name": "Name", "status": "Status", "email": "Email", "role": "Role", "createdOn": "Created on" },
|
"columns": { "name": "Name", "status": "Status", "email": "Email", "role": "Role", "createdOn": "Created on" },
|
||||||
"empty": "No people",
|
"empty": "No people",
|
||||||
"searchPlaceholder": "Search people...",
|
"searchPlaceholder": "Search people...",
|
||||||
"filters": { "allStatuses": "All statuses", "pending": "Pending", "active": "Active", "deactivated": "Deactivated", "allRoles": "All roles", "allTypes": "All types" }
|
"filters": { "allStatuses": "All statuses", "status": "Status", "pending": "Pending", "active": "Active", "deactivated": "Deactivated", "allRoles": "All roles", "allTypes": "All types" }
|
||||||
},
|
},
|
||||||
"peopleListItem": {
|
"peopleListItem": {
|
||||||
"messages": { "invitationSent": "Invitation sent successfully", "roleUpdated": "Role updated successfully", "deactivated": "Person deactivated successfully", "removed": "Person removed successfully" },
|
"messages": { "invitationSent": "Invitation sent successfully", "roleUpdated": "Role updated successfully", "deactivated": "Person deactivated successfully", "removed": "Person removed successfully" },
|
||||||
|
|||||||
@@ -2458,6 +2458,7 @@
|
|||||||
"searchPlaceholder": "Rechercher des personnes...",
|
"searchPlaceholder": "Rechercher des personnes...",
|
||||||
"filters": {
|
"filters": {
|
||||||
"allStatuses": "Tous les statuts",
|
"allStatuses": "Tous les statuts",
|
||||||
|
"status": "Statut",
|
||||||
"pending": "En attente",
|
"pending": "En attente",
|
||||||
"active": "Actif",
|
"active": "Actif",
|
||||||
"deactivated": "Désactivé",
|
"deactivated": "Désactivé",
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export const enrollDevicePageQuery = graphql`
|
|||||||
profiles(
|
profiles(
|
||||||
first: 1000
|
first: 1000
|
||||||
orderBy: { direction: ASC, field: ORGANIZATION_NAME }
|
orderBy: { direction: ASC, field: ORGANIZATION_NAME }
|
||||||
filter: { state: ACTIVE }
|
filter: { states: [ACTIVE] }
|
||||||
) @required(action: THROW) {
|
) @required(action: THROW) {
|
||||||
edges @required(action: THROW) {
|
edges @required(action: THROW) {
|
||||||
node @required(action: THROW) {
|
node @required(action: THROW) {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const membershipsPageQuery = graphql`
|
|||||||
profiles(
|
profiles(
|
||||||
first: 1000
|
first: 1000
|
||||||
orderBy: { direction: ASC, field: ORGANIZATION_NAME }
|
orderBy: { direction: ASC, field: ORGANIZATION_NAME }
|
||||||
filter: { state: ACTIVE }
|
filter: { states: [ACTIVE] }
|
||||||
)
|
)
|
||||||
@connection(key: "MembershipsPage_profiles")
|
@connection(key: "MembershipsPage_profiles")
|
||||||
@required(action: THROW) {
|
@required(action: THROW) {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const membershipsDropdownMenuQuery = graphql`
|
|||||||
profiles(
|
profiles(
|
||||||
first: 1000
|
first: 1000
|
||||||
orderBy: { direction: ASC, field: ORGANIZATION_NAME }
|
orderBy: { direction: ASC, field: ORGANIZATION_NAME }
|
||||||
filter: { state: ACTIVE }
|
filter: { states: [ACTIVE] }
|
||||||
) @required(action: THROW) {
|
) @required(action: THROW) {
|
||||||
edges @required(action: THROW) {
|
edges @required(action: THROW) {
|
||||||
node @required(action: THROW) {
|
node @required(action: THROW) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
import { getAssignableRoles, getMembershipRoles, peopleRoles } from "@probo/helpers";
|
import { getAssignableRoles, getMembershipRoles, peopleRoles } from "@probo/helpers";
|
||||||
import {
|
import {
|
||||||
|
Checkbox,
|
||||||
IconMagnifyingGlass,
|
IconMagnifyingGlass,
|
||||||
Input,
|
Input,
|
||||||
Option,
|
Option,
|
||||||
@@ -89,11 +90,13 @@ const fragment = graphql`
|
|||||||
|
|
||||||
type PeopleFilter = {
|
type PeopleFilter = {
|
||||||
query: string | null;
|
query: string | null;
|
||||||
state: ProfileState | null;
|
states: ProfileState[];
|
||||||
role: MembershipRole | null;
|
role: MembershipRole | null;
|
||||||
kind: string | null;
|
kind: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PROFILE_STATES: ProfileState[] = ["PENDING", "ACTIVE", "DEACTIVATED"];
|
||||||
|
|
||||||
export function PeopleList(props: {
|
export function PeopleList(props: {
|
||||||
fKey: PeopleListFragment$key;
|
fKey: PeopleListFragment$key;
|
||||||
onConnectionIdChange: (connectionId: string) => void;
|
onConnectionIdChange: (connectionId: string) => void;
|
||||||
@@ -105,7 +108,7 @@ export function PeopleList(props: {
|
|||||||
const canManageRoles = getAssignableRoles(role).length > 0;
|
const canManageRoles = getAssignableRoles(role).length > 0;
|
||||||
|
|
||||||
const [queryFilter, setQueryFilter] = useState<string | null>(null);
|
const [queryFilter, setQueryFilter] = useState<string | null>(null);
|
||||||
const [stateFilter, setStateFilter] = useState<ProfileState | null>(null);
|
const [statesFilter, setStatesFilter] = useState<ProfileState[]>([]);
|
||||||
const [roleFilter, setRoleFilter] = useState<MembershipRole | null>(null);
|
const [roleFilter, setRoleFilter] = useState<MembershipRole | null>(null);
|
||||||
const [kindFilter, setKindFilter] = useState<string | null>(null);
|
const [kindFilter, setKindFilter] = useState<string | null>(null);
|
||||||
const [order, setOrder] = useState<Order>({
|
const [order, setOrder] = useState<Order>({
|
||||||
@@ -127,7 +130,7 @@ export function PeopleList(props: {
|
|||||||
|
|
||||||
const currentFilter = (overrides: Partial<PeopleFilter> = {}): PeopleFilter => ({
|
const currentFilter = (overrides: Partial<PeopleFilter> = {}): PeopleFilter => ({
|
||||||
query: queryFilter,
|
query: queryFilter,
|
||||||
state: stateFilter,
|
states: statesFilter,
|
||||||
role: roleFilter,
|
role: roleFilter,
|
||||||
kind: kindFilter,
|
kind: kindFilter,
|
||||||
...overrides,
|
...overrides,
|
||||||
@@ -135,7 +138,7 @@ export function PeopleList(props: {
|
|||||||
|
|
||||||
const connectionFilter = (filter: PeopleFilter) => ({
|
const connectionFilter = (filter: PeopleFilter) => ({
|
||||||
query: filter.query,
|
query: filter.query,
|
||||||
state: filter.state,
|
states: filter.states.length > 0 ? filter.states : null,
|
||||||
role: filter.role,
|
role: filter.role,
|
||||||
kind: filter.kind,
|
kind: filter.kind,
|
||||||
contractEnded: null,
|
contractEnded: null,
|
||||||
@@ -170,7 +173,7 @@ export function PeopleList(props: {
|
|||||||
},
|
},
|
||||||
filter: {
|
filter: {
|
||||||
query: newQuery,
|
query: newQuery,
|
||||||
state: stateFilter,
|
states: statesFilter.length > 0 ? statesFilter : null,
|
||||||
role: roleFilter,
|
role: roleFilter,
|
||||||
kind: kindFilter,
|
kind: kindFilter,
|
||||||
contractEnded: null,
|
contractEnded: null,
|
||||||
@@ -180,7 +183,7 @@ export function PeopleList(props: {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[peoplePagination, order, stateFilter, roleFilter, kindFilter],
|
[peoplePagination, order, statesFilter, roleFilter, kindFilter],
|
||||||
),
|
),
|
||||||
SEARCH_DEBOUNCE_MS,
|
SEARCH_DEBOUNCE_MS,
|
||||||
);
|
);
|
||||||
@@ -190,10 +193,12 @@ export function PeopleList(props: {
|
|||||||
debouncedRefetchQuery(value);
|
debouncedRefetchQuery(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStateFilterChange = (value: string) => {
|
const handleStateFilterToggle = (state: ProfileState) => {
|
||||||
const newState = value === "ALL" ? null : (value as ProfileState);
|
const newStates = statesFilter.includes(state)
|
||||||
setStateFilter(newState);
|
? statesFilter.filter(s => s !== state)
|
||||||
refetchPeople({ state: newState });
|
: [...statesFilter, state];
|
||||||
|
setStatesFilter(newStates);
|
||||||
|
refetchPeople({ states: newStates });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRoleFilterChange = (value: string) => {
|
const handleRoleFilterChange = (value: string) => {
|
||||||
@@ -226,15 +231,18 @@ export function PeopleList(props: {
|
|||||||
value={queryFilter ?? ""}
|
value={queryFilter ?? ""}
|
||||||
onValueChange={handleQueryFilterChange}
|
onValueChange={handleQueryFilterChange}
|
||||||
/>
|
/>
|
||||||
<Select
|
<div className="flex items-center gap-3">
|
||||||
value={stateFilter ?? "ALL"}
|
<span className="text-sm text-txt-secondary">{t("peopleList.filters.status")}</span>
|
||||||
onValueChange={handleStateFilterChange}
|
{PROFILE_STATES.map(state => (
|
||||||
>
|
<label key={state} className="flex items-center gap-2 text-sm cursor-pointer">
|
||||||
<Option value="ALL">{t("peopleList.filters.allStatuses")}</Option>
|
<Checkbox
|
||||||
<Option value="PENDING">{t("peopleList.filters.pending")}</Option>
|
checked={statesFilter.includes(state)}
|
||||||
<Option value="ACTIVE">{t("peopleList.filters.active")}</Option>
|
onChange={() => handleStateFilterToggle(state)}
|
||||||
<Option value="DEACTIVATED">{t("peopleList.filters.deactivated")}</Option>
|
/>
|
||||||
</Select>
|
{t(`peopleList.filters.${state.toLowerCase()}`)}
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
<Select
|
<Select
|
||||||
value={roleFilter ?? "ALL"}
|
value={roleFilter ?? "ALL"}
|
||||||
onValueChange={handleRoleFilterChange}
|
onValueChange={handleRoleFilterChange}
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ export function PeopleListItem(props: {
|
|||||||
: [...availableRoles, profile.membership.role];
|
: [...availableRoles, profile.membership.role];
|
||||||
|
|
||||||
const isActive = profile.state === "ACTIVE";
|
const isActive = profile.state === "ACTIVE";
|
||||||
const isInactive = !isActive;
|
const isInactive = profile.state === "DEACTIVATED";
|
||||||
|
|
||||||
const canSendActivationMail = !isActive && profile.source !== "SCIM" && profile.canInvite;
|
const canSendActivationMail = !isActive && profile.source !== "SCIM" && profile.canInvite;
|
||||||
const canDeactivate = profile.canDeactivate && profile.source !== "SCIM" && profile.state !== "DEACTIVATED";
|
const canDeactivate = profile.canDeactivate && profile.source !== "SCIM" && profile.state !== "DEACTIVATED";
|
||||||
|
|||||||
@@ -46,10 +46,10 @@ export const documentLayoutQuery = graphql`
|
|||||||
...DocumentTitleFormFragment
|
...DocumentTitleFormFragment
|
||||||
...DocumentActionsDropdown_versionFragment
|
...DocumentActionsDropdown_versionFragment
|
||||||
...DocumentDetailsCard_versionFragment
|
...DocumentDetailsCard_versionFragment
|
||||||
signatures(first: 0 filter: { activeContract: true, profileState: ACTIVE }) {
|
signatures(first: 0 filter: { activeContract: true, profileStates: [ACTIVE] }) {
|
||||||
totalCount
|
totalCount
|
||||||
}
|
}
|
||||||
signedSignatures: signatures(first: 0 filter: { states: [SIGNED], activeContract: true, profileState: ACTIVE }) {
|
signedSignatures: signatures(first: 0 filter: { states: [SIGNED], activeContract: true, profileStates: [ACTIVE] }) {
|
||||||
totalCount
|
totalCount
|
||||||
}
|
}
|
||||||
approvalQuorums(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
|
approvalQuorums(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
|
||||||
@@ -91,10 +91,10 @@ export const documentLayoutQuery = graphql`
|
|||||||
...DocumentTitleFormFragment
|
...DocumentTitleFormFragment
|
||||||
...DocumentActionsDropdown_versionFragment
|
...DocumentActionsDropdown_versionFragment
|
||||||
...DocumentDetailsCard_versionFragment
|
...DocumentDetailsCard_versionFragment
|
||||||
signatures(first: 0 filter: { activeContract: true, profileState: ACTIVE }) {
|
signatures(first: 0 filter: { activeContract: true, profileStates: [ACTIVE] }) {
|
||||||
totalCount
|
totalCount
|
||||||
}
|
}
|
||||||
signedSignatures: signatures(first: 0 filter: { states: [SIGNED], activeContract: true, profileState: ACTIVE }) {
|
signedSignatures: signatures(first: 0 filter: { states: [SIGNED], activeContract: true, profileStates: [ACTIVE] }) {
|
||||||
totalCount
|
totalCount
|
||||||
}
|
}
|
||||||
approvalQuorums(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
|
approvalQuorums(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
|
||||||
|
|||||||
@@ -79,10 +79,10 @@ const fragment = graphql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
signatures(first: 0 filter: { activeContract: true, profileState: ACTIVE }) {
|
signatures(first: 0 filter: { activeContract: true, profileStates: [ACTIVE] }) {
|
||||||
totalCount
|
totalCount
|
||||||
}
|
}
|
||||||
signedSignatures: signatures(first: 0 filter: { states: [SIGNED], activeContract: true, profileState: ACTIVE }) {
|
signedSignatures: signatures(first: 0 filter: { states: [SIGNED], activeContract: true, profileStates: [ACTIVE] }) {
|
||||||
totalCount
|
totalCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ function PeopleList({
|
|||||||
signatureDocumentsDialogPeopleQuery,
|
signatureDocumentsDialogPeopleQuery,
|
||||||
{
|
{
|
||||||
organizationId,
|
organizationId,
|
||||||
filter: { contractEnded: false, state: "ACTIVE" },
|
filter: { contractEnded: false, states: ["ACTIVE"] },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export const documentSignaturesPageQuery = graphql`
|
|||||||
query DocumentSignaturesPageQuery($documentId: ID! $organizationId: ID! $versionId: ID! $versionSpecified: Boolean!) {
|
query DocumentSignaturesPageQuery($documentId: ID! $organizationId: ID! $versionId: ID! $versionSpecified: Boolean!) {
|
||||||
organization: node(id: $organizationId) {
|
organization: node(id: $organizationId) {
|
||||||
__typename
|
__typename
|
||||||
...DocumentSignatureList_peopleFragment @arguments(filter: { contractEnded: false, state: ACTIVE })
|
...DocumentSignatureList_peopleFragment @arguments(filter: { contractEnded: false, states: [ACTIVE] })
|
||||||
}
|
}
|
||||||
# We use this on /documents/:documentId
|
# We use this on /documents/:documentId
|
||||||
document: node(id: $documentId) @skip(if: $versionSpecified) {
|
document: node(id: $documentId) @skip(if: $versionSpecified) {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const versionFragment = graphql`
|
|||||||
@argumentDefinitions(
|
@argumentDefinitions(
|
||||||
count: { type: "Int", defaultValue: 1000 }
|
count: { type: "Int", defaultValue: 1000 }
|
||||||
cursor: { type: "CursorKey" }
|
cursor: { type: "CursorKey" }
|
||||||
signatureFilter: { type: "DocumentVersionSignatureFilter", defaultValue: { activeContract: true, profileState: ACTIVE } }
|
signatureFilter: { type: "DocumentVersionSignatureFilter", defaultValue: { activeContract: true, profileStates: [ACTIVE] } }
|
||||||
) {
|
) {
|
||||||
...DocumentSignaturePlaceholder_versionFragment
|
...DocumentSignaturePlaceholder_versionFragment
|
||||||
signatures(first: $count, after: $cursor, filter: $signatureFilter)
|
signatures(first: $count, after: $cursor, filter: $signatureFilter)
|
||||||
@@ -110,7 +110,7 @@ export function DocumentSignatureList(props: {
|
|||||||
|
|
||||||
const filter = {
|
const filter = {
|
||||||
activeContract: true,
|
activeContract: true,
|
||||||
profileState: "ACTIVE" as const,
|
profileStates: ["ACTIVE" as const],
|
||||||
...(selectedStates.length > 0 ? { states: selectedStates } : {}),
|
...(selectedStates.length > 0 ? { states: selectedStates } : {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -98,13 +98,12 @@ export const description: INodeProperties[] = [
|
|||||||
description: 'Whether to filter by active contract status',
|
description: 'Whether to filter by active contract status',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Profile State',
|
displayName: 'Profile States',
|
||||||
name: 'state',
|
name: 'state',
|
||||||
type: 'options',
|
type: 'multiOptions',
|
||||||
default: '',
|
default: [],
|
||||||
description: 'Filter by signatory profile state',
|
description: 'Filter by signatory profile states',
|
||||||
options: [
|
options: [
|
||||||
{ name: 'Any', value: '' },
|
|
||||||
{ name: 'Pending', value: 'PENDING' },
|
{ name: 'Pending', value: 'PENDING' },
|
||||||
{ name: 'Active', value: 'ACTIVE' },
|
{ name: 'Active', value: 'ACTIVE' },
|
||||||
{ name: 'Deactivated', value: 'DEACTIVATED' },
|
{ name: 'Deactivated', value: 'DEACTIVATED' },
|
||||||
@@ -126,7 +125,7 @@ export async function execute(
|
|||||||
const filter: IDataObject = {};
|
const filter: IDataObject = {};
|
||||||
if ((filters.states as string[])?.length) filter.states = filters.states;
|
if ((filters.states as string[])?.length) filter.states = filters.states;
|
||||||
if (filters.activeContract !== undefined) filter.activeContract = filters.activeContract;
|
if (filters.activeContract !== undefined) filter.activeContract = filters.activeContract;
|
||||||
if (filters.state) filter.profileState = filters.state;
|
if ((filters.state as string[])?.length) filter.profileStates = filters.state;
|
||||||
|
|
||||||
const hasFilter = Object.keys(filter).length > 0;
|
const hasFilter = Object.keys(filter).length > 0;
|
||||||
|
|
||||||
|
|||||||
@@ -85,9 +85,9 @@ export const description: INodeProperties[] = [
|
|||||||
description: 'Search users by full name or email address',
|
description: 'Search users by full name or email address',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'State',
|
displayName: 'States',
|
||||||
name: 'state',
|
name: 'state',
|
||||||
type: 'options',
|
type: 'multiOptions',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: ['user'],
|
resource: ['user'],
|
||||||
@@ -96,12 +96,11 @@ export const description: INodeProperties[] = [
|
|||||||
},
|
},
|
||||||
options: [
|
options: [
|
||||||
{ name: 'Active', value: 'ACTIVE' },
|
{ name: 'Active', value: 'ACTIVE' },
|
||||||
{ name: 'All', value: '' },
|
|
||||||
{ name: 'Deactivated', value: 'DEACTIVATED' },
|
{ name: 'Deactivated', value: 'DEACTIVATED' },
|
||||||
{ name: 'Pending', value: 'PENDING' },
|
{ name: 'Pending', value: 'PENDING' },
|
||||||
],
|
],
|
||||||
default: '',
|
default: [],
|
||||||
description: 'Filter by profile state',
|
description: 'Filter by profile states',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Role',
|
displayName: 'Role',
|
||||||
@@ -153,7 +152,7 @@ export async function execute(
|
|||||||
const returnAll = this.getNodeParameter('returnAll', itemIndex) as boolean;
|
const returnAll = this.getNodeParameter('returnAll', itemIndex) as boolean;
|
||||||
const limit = this.getNodeParameter('limit', itemIndex, 50) as number;
|
const limit = this.getNodeParameter('limit', itemIndex, 50) as number;
|
||||||
const query = this.getNodeParameter('query', itemIndex, '') as string;
|
const query = this.getNodeParameter('query', itemIndex, '') as string;
|
||||||
const state = this.getNodeParameter('state', itemIndex, '') as string;
|
const state = this.getNodeParameter('state', itemIndex, []) as string[];
|
||||||
const role = this.getNodeParameter('role', itemIndex, '') as string;
|
const role = this.getNodeParameter('role', itemIndex, '') as string;
|
||||||
const kind = this.getNodeParameter('kind', itemIndex, '') as string;
|
const kind = this.getNodeParameter('kind', itemIndex, '') as string;
|
||||||
|
|
||||||
@@ -194,8 +193,8 @@ export async function execute(
|
|||||||
if (query) {
|
if (query) {
|
||||||
filter.query = query;
|
filter.query = query;
|
||||||
}
|
}
|
||||||
if (state) {
|
if (state.length > 0) {
|
||||||
filter.state = state;
|
filter.states = state;
|
||||||
}
|
}
|
||||||
if (role) {
|
if (role) {
|
||||||
filter.role = role;
|
filter.role = role;
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
|
|||||||
flagOrder string
|
flagOrder string
|
||||||
flagOrderDir string
|
flagOrderDir string
|
||||||
flagContractEnded string
|
flagContractEnded string
|
||||||
flagState string
|
flagState []string
|
||||||
flagFilter string
|
flagFilter string
|
||||||
flagRole string
|
flagRole string
|
||||||
flagKind string
|
flagKind string
|
||||||
@@ -157,12 +157,14 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
|
|||||||
filter["contractEnded"] = flagContractEnded == "true"
|
filter["contractEnded"] = flagContractEnded == "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
if flagState != "" {
|
if len(flagState) > 0 {
|
||||||
if err := cmdutil.ValidateEnum("state", flagState, []string{"PENDING", "ACTIVE", "DEACTIVATED"}); err != nil {
|
for _, state := range flagState {
|
||||||
return err
|
if err := cmdutil.ValidateEnum("state", state, []string{"PENDING", "ACTIVE", "DEACTIVATED"}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filter["state"] = flagState
|
filter["states"] = flagState
|
||||||
}
|
}
|
||||||
|
|
||||||
if flagFilter != "" {
|
if flagFilter != "" {
|
||||||
@@ -273,7 +275,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
|
|||||||
cmd.Flags().StringVar(&flagOrder, "order-by", "", "Order by field (FULL_NAME, CREATED_AT, KIND)")
|
cmd.Flags().StringVar(&flagOrder, "order-by", "", "Order by field (FULL_NAME, CREATED_AT, KIND)")
|
||||||
cmd.Flags().StringVar(&flagOrderDir, "order-direction", "DESC", "Sort direction (ASC, DESC)")
|
cmd.Flags().StringVar(&flagOrderDir, "order-direction", "DESC", "Sort direction (ASC, DESC)")
|
||||||
cmd.Flags().StringVar(&flagContractEnded, "contract-ended", "", "Filter by contract status (true or false)")
|
cmd.Flags().StringVar(&flagContractEnded, "contract-ended", "", "Filter by contract status (true or false)")
|
||||||
cmd.Flags().StringVar(&flagState, "state", "", "Filter by profile state (PENDING, ACTIVE, DEACTIVATED)")
|
cmd.Flags().StringSliceVar(&flagState, "state", nil, "Filter by profile state; repeat or comma-separate for multiple (PENDING, ACTIVE, DEACTIVATED)")
|
||||||
cmd.Flags().StringVarP(&flagFilter, "filter", "q", "", "Filter users by name or email search query")
|
cmd.Flags().StringVarP(&flagFilter, "filter", "q", "", "Filter users by name or email search query")
|
||||||
cmd.Flags().StringVar(&flagRole, "role", "", "Filter by membership role (OWNER, ADMIN, VIEWER, AUDITOR, EMPLOYEE)")
|
cmd.Flags().StringVar(&flagRole, "role", "", "Filter by membership role (OWNER, ADMIN, VIEWER, AUDITOR, EMPLOYEE)")
|
||||||
cmd.Flags().StringVar(&flagKind, "kind", "", "Filter by profile kind (EMPLOYEE, CONTRACTOR, SERVICE_ACCOUNT)")
|
cmd.Flags().StringVar(&flagKind, "kind", "", "Filter by profile kind (EMPLOYEE, CONTRACTOR, SERVICE_ACCOUNT)")
|
||||||
|
|||||||
@@ -28,15 +28,15 @@ type (
|
|||||||
DocumentVersionSignatureFilter struct {
|
DocumentVersionSignatureFilter struct {
|
||||||
states DocumentVersionSignatureStates
|
states DocumentVersionSignatureStates
|
||||||
activeContract *bool
|
activeContract *bool
|
||||||
profileState *ProfileState
|
profileStates ProfileStateValues
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDocumentVersionSignatureFilter(states []DocumentVersionSignatureState, activeContract *bool, profileState *ProfileState) *DocumentVersionSignatureFilter {
|
func NewDocumentVersionSignatureFilter(states []DocumentVersionSignatureState, activeContract *bool, profileStates []ProfileState) *DocumentVersionSignatureFilter {
|
||||||
return &DocumentVersionSignatureFilter{
|
return &DocumentVersionSignatureFilter{
|
||||||
states: DocumentVersionSignatureStates(states),
|
states: DocumentVersionSignatureStates(states),
|
||||||
activeContract: activeContract,
|
activeContract: activeContract,
|
||||||
profileState: profileState,
|
profileStates: ProfileStateValues(profileStates),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ func (f *DocumentVersionSignatureFilter) SQLArguments() pgx.StrictNamedArgs {
|
|||||||
return pgx.StrictNamedArgs{
|
return pgx.StrictNamedArgs{
|
||||||
"states": f.states,
|
"states": f.states,
|
||||||
"active_contract": f.activeContract,
|
"active_contract": f.activeContract,
|
||||||
"profile_state": f.profileState,
|
"profile_states": f.profileStates,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,13 +81,13 @@ func (f *DocumentVersionSignatureFilter) SQLFragment() string {
|
|||||||
END
|
END
|
||||||
AND
|
AND
|
||||||
CASE
|
CASE
|
||||||
WHEN @profile_state::text IS NULL
|
WHEN @profile_states::membership_state[] IS NULL
|
||||||
THEN TRUE
|
THEN TRUE
|
||||||
ELSE EXISTS (
|
ELSE EXISTS (
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM iam_membership_profiles p
|
FROM iam_membership_profiles p
|
||||||
WHERE p.id = signed_by_profile_id
|
WHERE p.id = signed_by_profile_id
|
||||||
AND p.state = @profile_state::membership_state
|
AND p.state = ANY(@profile_states::membership_state[])
|
||||||
)
|
)
|
||||||
END
|
END
|
||||||
)`
|
)`
|
||||||
|
|||||||
@@ -81,11 +81,6 @@ func (f *MembershipProfileFilter) WithExternalID(externalID string) *MembershipP
|
|||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *MembershipProfileFilter) WithState(state ProfileState) *MembershipProfileFilter {
|
|
||||||
f.states = ProfileStateValues{state}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *MembershipProfileFilter) WithStates(states ...ProfileState) *MembershipProfileFilter {
|
func (f *MembershipProfileFilter) WithStates(states ...ProfileState) *MembershipProfileFilter {
|
||||||
f.states = ProfileStateValues(states)
|
f.states = ProfileStateValues(states)
|
||||||
return f
|
return f
|
||||||
|
|||||||
@@ -46,10 +46,3 @@ WHERE state = 'DEACTIVATED'
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
UPDATE iam_membership_profiles
|
|
||||||
SET activated_at = updated_at
|
|
||||||
WHERE state = 'ACTIVE';
|
|
||||||
|
|
||||||
UPDATE iam_membership_profiles
|
|
||||||
SET deactivated_at = NOW()
|
|
||||||
WHERE state = 'DEACTIVATED';
|
|
||||||
|
|||||||
@@ -405,7 +405,7 @@ func (s *OrganizationService) DeactivateUser(
|
|||||||
if membership.Role == coredata.MembershipRoleOwner && profile.State == coredata.ProfileStateActive {
|
if membership.Role == coredata.MembershipRoleOwner && profile.State == coredata.ProfileStateActive {
|
||||||
profiles := coredata.MembershipProfiles{}
|
profiles := coredata.MembershipProfiles{}
|
||||||
|
|
||||||
count, err := profiles.CountActiveOwnerByOrganizationID(ctx, tx, scope, organizationID)
|
count, err := profiles.CountActiveOwnerByOrganizationID(ctx, tx, scope, profile.OrganizationID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot count active owners: %w", err)
|
return fmt.Errorf("cannot count active owners: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -336,6 +336,10 @@ func (s *Service) HandleAssertion(
|
|||||||
if profile.State == coredata.ProfileStateDeactivated {
|
if profile.State == coredata.ProfileStateDeactivated {
|
||||||
return NewUserInactiveError(profile.ID)
|
return NewUserInactiveError(profile.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if profile.State == coredata.ProfileStatePending {
|
||||||
|
profile.MarkActive(now)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := membership.LoadByIdentityIDAndOrganizationID(
|
if err := membership.LoadByIdentityIDAndOrganizationID(
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ enum ProfileOrderField
|
|||||||
|
|
||||||
input ProfileFilter {
|
input ProfileFilter {
|
||||||
contractEnded: Boolean
|
contractEnded: Boolean
|
||||||
state: ProfileState
|
|
||||||
states: [ProfileState!]
|
states: [ProfileState!]
|
||||||
query: String
|
query: String
|
||||||
role: MembershipRole
|
role: MembershipRole
|
||||||
|
|||||||
@@ -36,10 +36,6 @@ func (r *identityResolver) Profiles(ctx context.Context, obj *types.Identity, fi
|
|||||||
filters.WithStates(filter.States...)
|
filters.WithStates(filter.States...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if filter.State != nil {
|
|
||||||
filters.WithState(*filter.State)
|
|
||||||
}
|
|
||||||
|
|
||||||
if filter.Query != nil {
|
if filter.Query != nil {
|
||||||
filters.WithQuery(filter.Query)
|
filters.WithQuery(filter.Query)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,10 +192,6 @@ func (r *organizationResolver) Profiles(ctx context.Context, obj *types.Organiza
|
|||||||
filters.WithStates(filter.States...)
|
filters.WithStates(filter.States...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if filter.State != nil {
|
|
||||||
filters.WithState(*filter.State)
|
|
||||||
}
|
|
||||||
|
|
||||||
if filter.Query != nil {
|
if filter.Query != nil {
|
||||||
filters.WithQuery(filter.Query)
|
filters.WithQuery(filter.Query)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ func (r *documentVersionResolver) Signatures(ctx context.Context, obj *types.Doc
|
|||||||
var (
|
var (
|
||||||
signatureStates []coredata.DocumentVersionSignatureState
|
signatureStates []coredata.DocumentVersionSignatureState
|
||||||
activeContract *bool
|
activeContract *bool
|
||||||
profileState *coredata.ProfileState
|
profileStates []coredata.ProfileState
|
||||||
)
|
)
|
||||||
|
|
||||||
if filter != nil {
|
if filter != nil {
|
||||||
@@ -294,12 +294,12 @@ func (r *documentVersionResolver) Signatures(ctx context.Context, obj *types.Doc
|
|||||||
activeContract = filter.ActiveContract
|
activeContract = filter.ActiveContract
|
||||||
}
|
}
|
||||||
|
|
||||||
if filter.ProfileState != nil {
|
if filter.ProfileStates != nil {
|
||||||
profileState = filter.ProfileState
|
profileStates = filter.ProfileStates
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
signatureFilter := coredata.NewDocumentVersionSignatureFilter(signatureStates, activeContract, profileState)
|
signatureFilter := coredata.NewDocumentVersionSignatureFilter(signatureStates, activeContract, profileStates)
|
||||||
|
|
||||||
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
||||||
|
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ input DocumentVersionSignatureOrder {
|
|||||||
input DocumentVersionSignatureFilter {
|
input DocumentVersionSignatureFilter {
|
||||||
states: [DocumentVersionSignatureState!]
|
states: [DocumentVersionSignatureState!]
|
||||||
activeContract: Boolean
|
activeContract: Boolean
|
||||||
profileState: ProfileState
|
profileStates: [ProfileState!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input DocumentVersionApprovalQuorumOrder {
|
input DocumentVersionApprovalQuorumOrder {
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ input ProfileOrder
|
|||||||
|
|
||||||
input ProfileFilter {
|
input ProfileFilter {
|
||||||
contractEnded: Boolean
|
contractEnded: Boolean
|
||||||
state: ProfileState
|
|
||||||
states: [ProfileState!]
|
states: [ProfileState!]
|
||||||
query: String
|
query: String
|
||||||
role: MembershipRole
|
role: MembershipRole
|
||||||
|
|||||||
@@ -123,10 +123,6 @@ func (r *organizationResolver) Profiles(ctx context.Context, obj *types.Organiza
|
|||||||
filters.WithStates(filter.States...)
|
filters.WithStates(filter.States...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if filter.State != nil {
|
|
||||||
filters.WithState(*filter.State)
|
|
||||||
}
|
|
||||||
|
|
||||||
if filter.Query != nil {
|
if filter.Query != nil {
|
||||||
filters.WithQuery(filter.Query)
|
filters.WithQuery(filter.Query)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2401,7 +2401,7 @@ func (r *Resolver) ListDocumentVersionSignaturesTool(ctx context.Context, req *m
|
|||||||
var (
|
var (
|
||||||
signatureStates []coredata.DocumentVersionSignatureState
|
signatureStates []coredata.DocumentVersionSignatureState
|
||||||
activeContract *bool
|
activeContract *bool
|
||||||
profileState *coredata.ProfileState
|
profileStates []coredata.ProfileState
|
||||||
)
|
)
|
||||||
|
|
||||||
if input.Filter != nil {
|
if input.Filter != nil {
|
||||||
@@ -2413,12 +2413,12 @@ func (r *Resolver) ListDocumentVersionSignaturesTool(ctx context.Context, req *m
|
|||||||
activeContract = input.Filter.ActiveContract
|
activeContract = input.Filter.ActiveContract
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.Filter.ProfileState != nil {
|
if input.Filter.ProfileStates != nil {
|
||||||
profileState = input.Filter.ProfileState
|
profileStates = input.Filter.ProfileStates
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
signatureFilter := coredata.NewDocumentVersionSignatureFilter(signatureStates, activeContract, profileState)
|
signatureFilter := coredata.NewDocumentVersionSignatureFilter(signatureStates, activeContract, profileStates)
|
||||||
|
|
||||||
page, err := prb.Documents.ListSignatures(ctx, scope, input.DocumentVersionID, cursor, signatureFilter)
|
page, err := prb.Documents.ListSignatures(ctx, scope, input.DocumentVersionID, cursor, signatureFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -2780,10 +2780,6 @@ func (r *Resolver) ListUsersTool(ctx context.Context, req *mcp.CallToolRequest,
|
|||||||
filter.WithStates(input.Filter.States...)
|
filter.WithStates(input.Filter.States...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.Filter.State != nil {
|
|
||||||
filter.WithState(*input.Filter.State)
|
|
||||||
}
|
|
||||||
|
|
||||||
if input.Filter.Query != nil {
|
if input.Filter.Query != nil {
|
||||||
filter.WithQuery(input.Filter.Query)
|
filter.WithQuery(input.Filter.Query)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -577,9 +577,6 @@ components:
|
|||||||
contract_ended:
|
contract_ended:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Filter by contract status. True returns only users with ended contracts, false returns only users with active or no contract.
|
description: Filter by contract status. True returns only users with ended contracts, false returns only users with active or no contract.
|
||||||
state:
|
|
||||||
$ref: "#/components/schemas/ProfileState"
|
|
||||||
description: Filter by profile state (PENDING, ACTIVE, or DEACTIVATED)
|
|
||||||
states:
|
states:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
@@ -6421,9 +6418,11 @@ components:
|
|||||||
active_contract:
|
active_contract:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Signatory contract status
|
description: Signatory contract status
|
||||||
profile_state:
|
profile_states:
|
||||||
$ref: "#/components/schemas/ProfileState"
|
type: array
|
||||||
description: Signatory profile state
|
items:
|
||||||
|
$ref: "#/components/schemas/ProfileState"
|
||||||
|
description: Signatory profile states
|
||||||
|
|
||||||
ListDocumentVersionSignaturesOutput:
|
ListDocumentVersionSignaturesOutput:
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
Reference in New Issue
Block a user