Moderation Checklist Fixes (#3986)
* fix: DEV-164 * fix: dev-163 * feat: DEV-162
This commit is contained in:
parent
6fb125cf0f
commit
058185c7fd
@ -116,8 +116,10 @@
|
||||
<span class="label__title">{{ action.label }}</span>
|
||||
</label>
|
||||
<DropdownSelect
|
||||
:max-visible-options="3"
|
||||
render-up
|
||||
:name="`dropdown-${getActionId(action)}`"
|
||||
:options="action.options"
|
||||
:options="getVisibleDropdownOptions(action)"
|
||||
:model-value="getDropdownValue(action)"
|
||||
:placeholder="'Select an option'"
|
||||
:disabled="false"
|
||||
@ -138,7 +140,7 @@
|
||||
<div class="mb-2 font-semibold">{{ action.label }}</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<ButtonStyled
|
||||
v-for="(option, optIndex) in action.options"
|
||||
v-for="(option, optIndex) in getVisibleMultiSelectOptions(action)"
|
||||
:key="`${getActionId(action)}-chip-${optIndex}`"
|
||||
:color="isChipSelected(action, optIndex) ? 'brand' : 'standard'"
|
||||
@click="toggleChip(action, optIndex)"
|
||||
@ -559,14 +561,20 @@ function handleKeybinds(event: KeyboardEvent) {
|
||||
},
|
||||
trySelectDropdownOption: (actionIndex: number, optionIndex: number) => {
|
||||
const action = visibleActions.value[actionIndex] as DropdownAction;
|
||||
if (action && action.type === "dropdown" && action.options[optionIndex]) {
|
||||
selectDropdownOption(action, action.options[optionIndex]);
|
||||
if (action && action.type === "dropdown") {
|
||||
const visibleOptions = getVisibleDropdownOptions(action);
|
||||
if (optionIndex < visibleOptions.length) {
|
||||
selectDropdownOption(action, visibleOptions[optionIndex]);
|
||||
}
|
||||
}
|
||||
},
|
||||
tryToggleChip: (actionIndex: number, chipIndex: number) => {
|
||||
const action = visibleActions.value[actionIndex] as MultiSelectChipsAction;
|
||||
if (action && action.type === "multi-select-chips") {
|
||||
toggleChip(action, chipIndex);
|
||||
const visibleOptions = getVisibleMultiSelectOptions(action);
|
||||
if (chipIndex < visibleOptions.length) {
|
||||
toggleChip(action, chipIndex);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -733,13 +741,17 @@ const multiSelectActions = computed(() =>
|
||||
|
||||
function getDropdownValue(action: DropdownAction) {
|
||||
const actionId = getActionId(action);
|
||||
const visibleOptions = getVisibleDropdownOptions(action);
|
||||
const currentValue = actionStates.value[actionId]?.value ?? action.defaultOption ?? 0;
|
||||
|
||||
if (action.options && action.options[currentValue]) {
|
||||
return action.options[currentValue];
|
||||
const allOptions = action.options;
|
||||
const storedOption = allOptions[currentValue];
|
||||
|
||||
if (storedOption && visibleOptions.includes(storedOption)) {
|
||||
return storedOption;
|
||||
}
|
||||
|
||||
return action.options?.[0] || null;
|
||||
return visibleOptions[0] || null;
|
||||
}
|
||||
|
||||
function isActionSelected(action: Action): boolean {
|
||||
@ -775,20 +787,31 @@ function selectDropdownOption(action: DropdownAction, selected: any) {
|
||||
function isChipSelected(action: MultiSelectChipsAction, optionIndex: number): boolean {
|
||||
const actionId = getActionId(action);
|
||||
const selectedSet = actionStates.value[actionId]?.value as Set<number> | undefined;
|
||||
return selectedSet?.has(optionIndex) || false;
|
||||
|
||||
const visibleOptions = getVisibleMultiSelectOptions(action);
|
||||
const visibleOption = visibleOptions[optionIndex];
|
||||
const originalIndex = action.options.findIndex((opt) => opt === visibleOption);
|
||||
|
||||
return selectedSet?.has(originalIndex) || false;
|
||||
}
|
||||
|
||||
function toggleChip(action: MultiSelectChipsAction, optionIndex: number) {
|
||||
const actionId = getActionId(action);
|
||||
const state = actionStates.value[actionId];
|
||||
if (state && state.value instanceof Set) {
|
||||
if (state.value.has(optionIndex)) {
|
||||
state.value.delete(optionIndex);
|
||||
} else {
|
||||
state.value.add(optionIndex);
|
||||
const visibleOptions = getVisibleMultiSelectOptions(action);
|
||||
const visibleOption = visibleOptions[optionIndex];
|
||||
const originalIndex = action.options.findIndex((opt) => opt === visibleOption);
|
||||
|
||||
if (originalIndex !== -1) {
|
||||
if (state.value.has(originalIndex)) {
|
||||
state.value.delete(originalIndex);
|
||||
} else {
|
||||
state.value.add(originalIndex);
|
||||
}
|
||||
state.selected = state.value.size > 0;
|
||||
persistState();
|
||||
}
|
||||
state.selected = state.value.size > 0;
|
||||
persistState();
|
||||
}
|
||||
}
|
||||
|
||||
@ -869,9 +892,23 @@ async function processAction(
|
||||
stageIndex: number,
|
||||
messageParts: MessagePart[],
|
||||
) {
|
||||
const allValidActionIds: string[] = [];
|
||||
checklist.forEach((stage, stageIdx) => {
|
||||
stage.actions.forEach((stageAction, actionIdx) => {
|
||||
allValidActionIds.push(getActionIdForStage(stageAction, stageIdx, actionIdx));
|
||||
if (stageAction.enablesActions) {
|
||||
stageAction.enablesActions.forEach((enabledAction, enabledIdx) => {
|
||||
allValidActionIds.push(
|
||||
getActionIdForStage(enabledAction, stageIdx, actionIdx, enabledIdx),
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (action.type === "button" || action.type === "toggle") {
|
||||
const buttonAction = action as ButtonAction | ToggleAction;
|
||||
const message = await getActionMessage(buttonAction, selectedActionIds);
|
||||
const message = await getActionMessage(buttonAction, selectedActionIds, allValidActionIds);
|
||||
if (message) {
|
||||
messageParts.push({
|
||||
weight: buttonAction.weight,
|
||||
@ -885,6 +922,7 @@ async function processAction(
|
||||
const matchingVariant = findMatchingVariant(
|
||||
conditionalAction.messageVariants,
|
||||
selectedActionIds,
|
||||
allValidActionIds,
|
||||
);
|
||||
if (matchingVariant) {
|
||||
const message = (await matchingVariant.message()) as string;
|
||||
@ -944,6 +982,24 @@ function shouldShowAction(action: Action): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
function getVisibleDropdownOptions(action: DropdownAction) {
|
||||
return action.options.filter((option) => {
|
||||
if (typeof option.shouldShow === "function") {
|
||||
return option.shouldShow(props.project);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function getVisibleMultiSelectOptions(action: MultiSelectChipsAction) {
|
||||
return action.options.filter((option) => {
|
||||
if (typeof option.shouldShow === "function") {
|
||||
return option.shouldShow(props.project);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function shouldShowStageIndex(stageIndex: number): boolean {
|
||||
return shouldShowStage(checklist[stageIndex]);
|
||||
}
|
||||
|
||||
@ -153,6 +153,13 @@ export interface DropdownActionOption extends WeightedMessage {
|
||||
* The label of the option, which is displayed to the moderator.
|
||||
*/
|
||||
label: string
|
||||
|
||||
/**
|
||||
* A function that determines whether this option should be shown for a given project.
|
||||
*
|
||||
* By default, it returns `true`, meaning the option is always shown.
|
||||
*/
|
||||
shouldShow?: (project: Project) => boolean
|
||||
}
|
||||
|
||||
export interface DropdownAction extends BaseAction {
|
||||
@ -179,6 +186,13 @@ export interface MultiSelectChipsOption extends WeightedMessage {
|
||||
* The label of the chip, which is displayed to the moderator.
|
||||
*/
|
||||
label: string
|
||||
|
||||
/**
|
||||
* A function that determines whether this option should be shown for a given project.
|
||||
*
|
||||
* By default, it returns `true`, meaning the option is always shown.
|
||||
*/
|
||||
shouldShow?: (project: Project) => boolean
|
||||
}
|
||||
|
||||
export interface MultiSelectChipsAction extends BaseAction {
|
||||
|
||||
@ -125,13 +125,19 @@ export function processMessage(
|
||||
export function findMatchingVariant(
|
||||
variants: ConditionalMessage[],
|
||||
selectedActionIds: string[],
|
||||
allValidActionIds?: string[],
|
||||
): ConditionalMessage | null {
|
||||
for (const variant of variants) {
|
||||
const conditions = variant.conditions
|
||||
|
||||
const meetsRequired =
|
||||
!conditions.requiredActions ||
|
||||
conditions.requiredActions.every((id) => selectedActionIds.includes(id))
|
||||
conditions.requiredActions.every((id) => {
|
||||
if (allValidActionIds && !allValidActionIds.includes(id)) {
|
||||
return false
|
||||
}
|
||||
return selectedActionIds.includes(id)
|
||||
})
|
||||
|
||||
const meetsExcluded =
|
||||
!conditions.excludedActions ||
|
||||
@ -148,9 +154,14 @@ export function findMatchingVariant(
|
||||
export async function getActionMessage(
|
||||
action: ButtonAction | ToggleAction,
|
||||
selectedActionIds: string[],
|
||||
allValidActionIds?: string[],
|
||||
): Promise<string> {
|
||||
if (action.conditionalMessages && action.conditionalMessages.length > 0) {
|
||||
const matchingConditional = findMatchingVariant(action.conditionalMessages, selectedActionIds)
|
||||
const matchingConditional = findMatchingVariant(
|
||||
action.conditionalMessages,
|
||||
selectedActionIds,
|
||||
allValidActionIds,
|
||||
)
|
||||
if (matchingConditional) {
|
||||
return (await matchingConditional.message()) as string
|
||||
}
|
||||
|
||||
@ -103,6 +103,10 @@ const props = defineProps({
|
||||
type: Function,
|
||||
default: undefined,
|
||||
},
|
||||
maxVisibleOptions: {
|
||||
type: Number,
|
||||
default: undefined,
|
||||
},
|
||||
})
|
||||
|
||||
function getOptionLabel(option) {
|
||||
@ -263,7 +267,7 @@ const isChildOfDropdown = (element) => {
|
||||
|
||||
.options {
|
||||
z-index: 10;
|
||||
max-height: 18.75rem;
|
||||
max-height: v-bind('maxVisibleOptions ? `calc(${maxVisibleOptions} * 3rem)` : "18.75rem"');
|
||||
overflow-y: auto;
|
||||
box-shadow:
|
||||
var(--shadow-inset-sm),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user