Skip to content
Open
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
25 changes: 23 additions & 2 deletions internal/portal/src/common/Checkbox/Checkbox.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,21 @@
}

&:disabled ~ .checkbox__checkmark {
background-color: var(--disabled-bg);
border-color: var(--disabled-border);
background-color: var(
--disabled-bg,
var(--colors-background-neutral-subtle)
);
border-color: var(--disabled-border, var(--colors-border-neutral-subtle));
cursor: not-allowed;
opacity: 0.6;
}

&:checked:disabled ~ .checkbox__checkmark {
background-color: var(--colors-background-primary);
opacity: 0.6;
}

&:disabled {
cursor: not-allowed;
}
}
Expand Down Expand Up @@ -87,6 +100,14 @@
}
}

&:has(input:disabled) {
cursor: not-allowed;

.checkbox__label {
opacity: 0.6;
}
}

&__error {
position: absolute;
bottom: -20px;
Expand Down
3 changes: 1 addition & 2 deletions internal/portal/src/common/TopicPicker/TopicPicker.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
.topic-picker {
border: 1px solid var(--colors-outline-neutral);
border-radius: var(--radius-m);
max-height: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
max-height: 100%;

&__header {
border-bottom: 1px solid var(--colors-outline-neutral);
Expand Down
47 changes: 27 additions & 20 deletions internal/portal/src/common/TopicPicker/TopicPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,32 @@ interface TopicPickerProps {

const detectSeparator = (topics: string[]): string => {
// Common separators to check
const possibleSeparators = ['/', '.', '-'];
const possibleSeparators = ["/", ".", "-"];

// Find the first separator that appears in all topics
// and is the first occurring separator in each topic
return possibleSeparators.find(sep =>
topics.every(topic => {
const sepIndex = topic.indexOf(sep);
if (sepIndex === -1) return false;

// Check if any other separator appears before this one
const otherSepsIndex = possibleSeparators
.filter(s => s !== sep)
.map(s => topic.indexOf(s))
.filter(idx => idx !== -1);

return otherSepsIndex.every(idx => idx === -1 || idx > sepIndex);
})
) || '-'; // Fallback to '-' if no consistent separator is found
return (
possibleSeparators.find((sep) =>
topics.every((topic) => {
const sepIndex = topic.indexOf(sep);
if (sepIndex === -1) return false;

// Check if any other separator appears before this one
const otherSepsIndex = possibleSeparators
.filter((s) => s !== sep)
.map((s) => topic.indexOf(s))
.filter((idx) => idx !== -1);

return otherSepsIndex.every((idx) => idx === -1 || idx > sepIndex);
})
) || "-"
); // Fallback to '-' if no consistent separator is found
};

const topics: Topic[] = (() => {
const topicsList = CONFIGS.TOPICS.split(",");
const separator = detectSeparator(topicsList);

return topicsList.map((topic) => {
const parts = topic.split(separator);
return {
Expand Down Expand Up @@ -174,20 +176,25 @@ const TopicPicker = ({
.split(" ")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ")}`}
checked={areAllSelected}
indeterminate={isIndeterminate}
checked={isEverythingSelected || areAllSelected}
indeterminate={!isEverythingSelected && isIndeterminate}
onChange={() => toggleCategorySelection(categoryTopics)}
disabled={isEverythingSelected}
/>
</div>
{isExpanded && (
<div className="topic-picker__topics">
{categoryTopics.map((topic) => (
<div key={topic.id} className="topic-picker__topic">
<Checkbox
checked={selectedTopics.indexOf(topic.id) !== -1}
checked={
isEverythingSelected ||
selectedTopics.indexOf(topic.id) !== -1
}
onChange={() => toggleTopic(topic.id)}
label={topic.id}
monospace
disabled={isEverythingSelected}
/>
</div>
))}
Expand Down
57 changes: 39 additions & 18 deletions internal/portal/src/scenes/CreateDestination/CreateDestination.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
grid-template-columns: 272px 1fr;
width: 100%;
gap: 132px;
padding-top: calc(var(--base-grid-multiplier) * 18);
margin: 0 auto;
flex-direction: row;
height: 100vh;
padding-top: calc(var(--base-grid-multiplier) * 18);
height: calc(100vh - calc(var(--base-grid-multiplier) * 18));

&__sidebar {
display: flex;
Expand Down Expand Up @@ -55,58 +55,79 @@
}

&__step {
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
padding-bottom: var(--spacing-16);
box-sizing: border-box;

h1 {
margin-top: 64px;
margin-bottom: 0;
~ p {
margin-top: 0;
&__header {
padding-left: 4px;
padding-right: 4px;

h1 {
margin-top: 64px;
margin-bottom: 0;
~ p {
margin-top: 0;
}
}
}

form {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;

> div {
padding: 4px;
}
}

&__fields {
flex: 0 1 auto;
max-height: 100%;
min-height: 0;
}

&__actions {
margin-top: var(--spacing-8);
display: flex;
justify-content: flex-end;
flex-shrink: 0;
}
}
}

.destination-types {
display: flex;
flex-direction: column;
gap: 1rem;
border: 1px solid var(--colors-outline-neutral);
border-radius: var(--radius-m);
overflow: hidden;
height: 100%;

&__container {
overflow-y: auto;
display: flex;
flex-direction: column;
}
}

.destination-type-card {
.destination-type-option {
cursor: pointer;
position: relative;
border: 1px solid var(--colors-outline-neutral);
border-radius: var(--radius-m);
border-bottom: 1px solid var(--colors-outline-neutral);

&:last-child {
border-bottom: none;
}

&:has(input[type="radio"]:checked) {
border-color: var(--colors-outline-primary-focus);
outline: 1px solid var(--colors-outline-primary-focus);
background-color: var(--colors-background-hover);
}

&:hover {
border-color: var(--colors-outline-primary-focus);
outline: 1px solid var(--colors-outline-primary-focus);
background-color: var(--colors-background-hover);
}

input[type="radio"] {
Expand Down
81 changes: 52 additions & 29 deletions internal/portal/src/scenes/CreateDestination/CreateDestination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Step = {
FormFields: (props: {
defaultValue: Record<string, any>;
onChange: (value: Record<string, any>) => void;
destinations?: DestinationTypeReference[];
}) => React.ReactNode;
action: string;
};
Expand Down Expand Up @@ -84,35 +85,39 @@ const DESTINATION_TYPE_STEP: Step = {
FormFields: ({
destinations,
defaultValue,
onChange,
}: {
destinations: DestinationTypeReference[];
destinations?: DestinationTypeReference[];
defaultValue: Record<string, any>;
onChange?: (value: Record<string, any>) => void;
}) => (
<div className="destination-types">
{destinations?.map((destination) => (
<label key={destination.type} className="destination-type-card">
<input
type="radio"
name="type"
value={destination.type}
required
className="destination-type-radio"
defaultChecked={
defaultValue ? defaultValue.type === destination.type : undefined
}
/>
<div className="destination-type-content">
<h3 className="subtitle-l">
<span
className="destination-type-content__icon"
dangerouslySetInnerHTML={{ __html: destination.icon }}
/>{" "}
{destination.label}
</h3>
<p className="body-m muted">{destination.description}</p>
</div>
</label>
))}
<div className="destination-types__container">
{destinations?.map((destination) => (
<label key={destination.type} className="destination-type-option">
<input
type="radio"
name="type"
value={destination.type}
required
className="destination-type-radio"
defaultChecked={
defaultValue ? defaultValue.type === destination.type : undefined
}
/>
<div className="destination-type-content">
<h3 className="subtitle-l">
<span
className="destination-type-content__icon"
dangerouslySetInnerHTML={{ __html: destination.icon }}
/>{" "}
{destination.label}
</h3>
<p className="body-m muted">{destination.description}</p>
</div>
</label>
))}
</div>
</div>
),
action: "Next",
Expand All @@ -122,12 +127,18 @@ const CONFIGURATION_STEP: Step = {
title: "Configure destination",
sidebar_shortname: "Configure destination",
description: "Configure the destination you want to send to your destination",
isValid: (values: Record<string, any>) => {
// Form validation will be handled by the form's native validation
return true;
},
FormFields: ({
defaultValue,
destinations,
onChange,
}: {
defaultValue: Record<string, any>;
destinations: DestinationTypeReference[];
destinations?: DestinationTypeReference[];
onChange?: (value: Record<string, any>) => void;
}) => {
const destinationType = destinations?.find(
(d) => d.type === defaultValue.type
Expand Down Expand Up @@ -164,6 +175,15 @@ export default function CreateDestination() {
const currentStep = steps[currentStepIndex];
const nextStep = steps[currentStepIndex + 1] || null;

// Validate the current step when it changes or stepValues change
useEffect(() => {
if (currentStep.isValid) {
setIsValid(currentStep.isValid(stepValues));
} else {
setIsValid(false);
}
}, [currentStepIndex, stepValues, currentStep]);

const createDestination = (values: Record<string, any>) => {
setIsCreating(true);

Expand Down Expand Up @@ -244,16 +264,19 @@ export default function CreateDestination() {
</div>

<div className="create-destination__step">
<h1 className="title-xl">{currentStep.title}</h1>
<p className="body-m muted">{currentStep.description}</p>
<div className="create-destination__step__header">
<h1 className="title-xl">{currentStep.title}</h1>
<p className="body-m muted">{currentStep.description}</p>
</div>
<form
key={currentStepIndex}
onChange={(e) => {
const formData = new FormData(e.currentTarget);
const values = Object.fromEntries(formData.entries());
const allValues = { ...stepValues, ...values };

if (currentStep.isValid) {
setIsValid(currentStep.isValid(values));
setIsValid(currentStep.isValid(allValues));
} else {
setIsValid(e.currentTarget.checkValidity());
}
Expand Down