Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -523,9 +523,17 @@ export class RuleComparatorService {
}

private getSecondValueName(rule: RuleDto): string {
return rule.lastVal
? this.ruleConstanstService.getValueHumanName(rule.lastVal)
: this.ruleConstanstService.getCustomValueIdentifier(rule.customVal).type;
if (rule.lastVal) {
return this.ruleConstanstService.getValueHumanName(rule.lastVal);
}
// Unary actions (EXISTS / NOT_EXISTS) have neither a second value nor a
// custom value. Guard against it so this diagnostic-only helper never
// throws while logging a skipped comparison (which would abort the run).
if (rule.customVal) {
return this.ruleConstanstService.getCustomValueIdentifier(rule.customVal)
.type;
}
return 'none';
}

private handleSectionAction(sectionActionAnd: boolean) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,52 @@ describe('RuleComparatorService.executeRulesWithData', () => {

expect(result.data).toEqual([]);
});

it('completes the run and logs a skip (not a crash) when a unary rule value is unavailable', async () => {
// getCustomValueIdentifier dereferences customValue.ruleTypeId. A unary
// rule (EXISTS/NOT_EXISTS) carries no customVal, so logging the skipped
// comparison must not reach this helper — otherwise the run threw
// "Cannot read properties of undefined (reading 'ruleTypeId')" and aborted.
ruleConstanstService.getCustomValueIdentifier.mockImplementation(
(customValue: { ruleTypeId: number; value: string }) => ({
type: ['number', 'date', 'text', 'boolean', 'text list'][
customValue.ruleTypeId
],
value: customValue.value,
}),
);

const mediaItem = createSingleMedia();
const rules = [
createStoredRule(1, {
operator: null,
action: RulePossibility.NOT_EXISTS,
firstVal: [Application.PLEX, 6],
section: 0,
}),
];

mockGetterSequence(undefined);

const result = await ruleComparatorService.executeRulesWithData(
createRulesDto({ dataType: 'movie', rules }),
[mediaItem],
);

// The run completes (the bug aborted via the catch and returned undefined),
expect(result).toBeDefined();
// the item is not matched,
expect(result.data).toEqual([]);
// and the skip is logged rather than crashing the run.
expect(logger.debug).toHaveBeenCalledWith(
expect.stringContaining(
'Skipping rule comparison because a value is unavailable',
),
);
expect(logger.log).not.toHaveBeenCalledWith(
expect.stringContaining('Something went wrong'),
);
});
});

describe('OR sections', () => {
Expand Down
126 changes: 60 additions & 66 deletions apps/ui/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -632,84 +632,78 @@
.prose a:hover {
color: #8d4c1c;
}
.react-select-container {
@apply w-full;
}

.react-select-container .react-select__control {
@apply rounded-md border border-zinc-500 bg-zinc-700 text-white hover:border-zinc-500;
}

.react-select-container-dark .react-select__control {
@apply border border-zinc-700 bg-zinc-800;
}

.react-select-container .react-select__control--is-focused {
@apply rounded-md border border-zinc-500 bg-zinc-700 text-white shadow-sm;
}

.react-select-container-dark .react-select__control--is-focused {
@apply border-zinc-600 bg-zinc-800;
}

.react-select-container .react-select__menu {
@apply bg-zinc-700 text-zinc-300;
}

.react-select-container-dark .react-select__menu {
@apply bg-zinc-800;
}
}

.react-select-container .react-select__option--is-focused {
@apply bg-zinc-600 text-white;
}
/*
react-select's control/menu styles come from emotion, which is unlayered and
therefore wins over @layer components under Tailwind v4. Keep this theme
unlayered (like .prose above) so the dark field styling keeps winning;
otherwise the control falls back to react-select's white default.
*/
.react-select-container {
@apply w-full;
}

.react-select-container-dark .react-select__option--is-focused {
@apply bg-zinc-700;
}
/* Control mirrors the native field (the input/select rule in this file):
same border, background, radius, text size, and shadow. The :hover variant
matches the base so, like a native field, hovering never changes the border. */
.react-select-container .react-select__control,
.react-select-container .react-select__control:hover {
@apply rounded-md border border-zinc-500 bg-zinc-700 text-sm text-white shadow-xs;
}

.react-select-container .react-select__indicator-separator {
@apply bg-zinc-500;
}
/* Focus: maintainerr border, no ring (react-select paints a blue box-shadow
ring by default) — mirrors the field's focus:border-maintainerr-600 + ring-0.
The :hover variant keeps the focus colour while hovering. */
.react-select-container .react-select__control--is-focused,
.react-select-container .react-select__control--is-focused:hover {
@apply border-maintainerr-600 shadow-none;
}

.react-select-container .react-select__indicator {
@apply text-zinc-500;
}
/* Selected value, placeholder, and typed input: same size/weight/colour as a
native field (not bold, muted placeholder). */
.react-select-container .react-select__single-value,
.react-select-container .react-select__input-container {
@apply text-sm text-white;
}

.react-select-container .react-select__placeholder {
@apply text-zinc-400;
}
.react-select-container .react-select__placeholder {
@apply text-sm text-zinc-400;
}

.react-select-container .react-select__multi-value {
@apply rounded-md border border-zinc-500 bg-zinc-800;
}
/* The inner <input> must stay borderless/ringless, otherwise the forms plugin
gives it its own nested field box. */
.react-select-container .react-select__input,
.react-select-container .react-select__input input,
.react-select-container .react-select__input input:focus {
@apply border-none shadow-none ring-0;
}

.react-select-container .react-select__multi-value__label {
@apply text-white;
}
/* Muted chevron, no separator (native selects have neither). */
.react-select-container .react-select__indicator {
@apply text-zinc-400;
}

.react-select-container .react-select__multi-value__remove {
@apply cursor-pointer rounded-r-md hover:bg-red-700 hover:text-red-100;
}
.react-select-container .react-select__indicator-separator {
@apply hidden;
}

.react-select-container .react-select__input {
@apply border-none text-base shadow-xs ring-0;
}
/* Menu is a step darker than the control (bg-zinc-800), matching the native
select dropdown in the rule settings: no border, just a shadow. */
.react-select-container .react-select__menu {
@apply mt-1 rounded-md bg-zinc-800 text-sm text-white shadow-lg;
}

.react-select-container .react-select__input input:focus {
@apply border-none;
box-shadow: none;
}
.react-select-container .react-select__option {
@apply text-zinc-200;
}

.react-select-container .react-select__input-container {
@apply text-white;
}
.react-select-container .react-select__option--is-focused {
@apply bg-zinc-700 text-white;
}

.react-select-container .react-select__input-container,
.react-select-container .react-select__placeholder,
.react-select-container .react-select__single-value {
@apply font-semibold text-white;
}
.react-select-container .react-select__option--is-selected {
@apply bg-maintainerr-600 text-white;
}

@layer utilities {
Expand Down