(null);
- const handleCardClick = (selectedType: string) => {
- setSelectedType(selectedType);
- };
return (
{
color={theme.font.color.tertiary}
/>
}
- onClick={() => handleCardClick('Standard')}
- >
+ onClick={() => onTypeSelect?.('Standard')}
+ />
{
color={theme.font.color.tertiary}
/>
}
- onClick={() => handleCardClick('Custom')}
- >
+ onClick={() => onTypeSelect?.('Custom')}
+ />
{
color={theme.font.color.tertiary}
/>
}
- >
+ />
);
};
diff --git a/front/src/modules/settings/data-model/new-object/components/SettingsObjectTypeCard.tsx b/front/src/modules/settings/data-model/new-object/components/SettingsObjectTypeCard.tsx
index 9703215ae..c2ddf18c7 100644
--- a/front/src/modules/settings/data-model/new-object/components/SettingsObjectTypeCard.tsx
+++ b/front/src/modules/settings/data-model/new-object/components/SettingsObjectTypeCard.tsx
@@ -58,21 +58,19 @@ export const SettingsObjectTypeCard = ({
}: SettingsObjectTypeCardProps) => {
const theme = useTheme();
return (
- onclick}>
-
- {prefixIcon}
-
- {soon && }
- {!disabled && selected && }
-
-
+
+ {prefixIcon}
+
+ {soon && }
+ {!disabled && selected && }
+
);
};
diff --git a/front/src/modules/settings/data-model/object-edit/SettingsObjectIconSection.tsx b/front/src/modules/settings/data-model/object-edit/SettingsObjectIconSection.tsx
index 40a349b8c..69c1048f8 100644
--- a/front/src/modules/settings/data-model/object-edit/SettingsObjectIconSection.tsx
+++ b/front/src/modules/settings/data-model/object-edit/SettingsObjectIconSection.tsx
@@ -1,5 +1,6 @@
import styled from '@emotion/styled';
+import { IconPigMoney } from '@/ui/display/icon';
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
import { H2Title } from '@/ui/display/typography/components/H2Title';
import { IconPicker } from '@/ui/input/components/IconPicker';
@@ -23,15 +24,19 @@ const StyledArrowContainer = styled.div`
`;
type SettingsObjectIconSectionProps = {
- Icon: IconComponent;
- iconKey: string;
- setIconPicker?: (icon: { Icon: IconComponent; iconKey: string }) => void;
+ disabled?: boolean;
+ Icon?: IconComponent;
+ iconKey?: string;
+ label?: string;
+ onChange?: (icon: { Icon: IconComponent; iconKey: string }) => void;
};
export const SettingsObjectIconSection = ({
- Icon,
- iconKey,
- setIconPicker,
+ disabled,
+ Icon = IconPigMoney,
+ iconKey = 'IconPigMoney',
+ label,
+ onChange,
}: SettingsObjectIconSectionProps) => {
return (
@@ -41,15 +46,16 @@ export const SettingsObjectIconSection = ({
/>
{
- setIconPicker?.({ Icon: icon.Icon, iconKey: icon.iconKey });
+ onChange?.({ Icon: icon.Icon, iconKey: icon.iconKey });
}}
/>
-
+
);
diff --git a/front/src/modules/ui/display/icon/index.ts b/front/src/modules/ui/display/icon/index.ts
index 456fb1892..2a528203a 100644
--- a/front/src/modules/ui/display/icon/index.ts
+++ b/front/src/modules/ui/display/icon/index.ts
@@ -69,6 +69,7 @@ export {
IconNumbers,
IconPencil,
IconPhone,
+ IconPigMoney,
IconPlane,
IconPlug,
IconPlus,
diff --git a/front/src/modules/ui/input/components/IconPicker.tsx b/front/src/modules/ui/input/components/IconPicker.tsx
index 9c1759ffe..b29b00e7a 100644
--- a/front/src/modules/ui/input/components/IconPicker.tsx
+++ b/front/src/modules/ui/input/components/IconPicker.tsx
@@ -17,6 +17,7 @@ import { DropdownMenuSkeletonItem } from '../relation-picker/components/skeleton
import { IconPickerHotkeyScope } from '../types/IconPickerHotkeyScope';
type IconPickerProps = {
+ disabled?: boolean;
onChange: (params: { iconKey: string; Icon: IconComponent }) => void;
selectedIconKey?: string;
onClickOutside?: () => void;
@@ -39,6 +40,7 @@ const convertIconKeyToLabel = (iconKey: string) =>
iconKey.replace(/[A-Z]/g, (letter) => ` ${letter}`).trim();
export const IconPicker = ({
+ disabled,
onChange,
selectedIconKey,
onClickOutside,
@@ -81,6 +83,7 @@ export const IconPicker = ({
dropdownHotkeyScope={{ scope: IconPickerHotkeyScope.IconPicker }}
clickableComponent={
@@ -100,6 +103,7 @@ export const IconPicker = ({
{iconKeys.map((iconKey) => (
void;
+ placeholder?: string;
+ value?: string;
+};
+
+const StyledTextArea = styled(TextareaAutosize)`
+ background-color: ${({ theme }) => theme.background.transparent.lighter};
+ border: 1px solid ${({ theme }) => theme.border.color.medium};
+ border-radius: ${({ theme }) => theme.border.radius.sm};
+ box-sizing: border-box;
+ color: ${({ theme }) => theme.font.color.primary};
+ font-family: inherit;
+ font-size: ${({ theme }) => theme.font.size.md};
+ font-weight: ${({ theme }) => theme.font.weight.regular};
+ line-height: 16px;
+ overflow: auto;
+ padding: ${({ theme }) => theme.spacing(2)};
+ padding-top: ${({ theme }) => theme.spacing(3)};
+ resize: none;
+ width: 100%;
+
+ &:focus {
+ outline: none;
+ }
+
+ &::placeholder {
+ color: ${({ theme }) => theme.font.color.light};
+ font-weight: ${({ theme }) => theme.font.weight.regular};
+ }
+
+ &:disabled {
+ color: ${({ theme }) => theme.font.color.tertiary};
+ }
+`;
+
+export const TextArea = ({
+ disabled,
+ placeholder,
+ minRows = 1,
+ value = '',
+ onChange,
+}: TextAreaProps) => {
+ const computedMinRows = Math.min(minRows, MAX_ROWS);
+
+ return (
+ onChange?.(event.target.value)}
+ disabled={disabled}
+ />
+ );
+};
diff --git a/front/src/modules/ui/input/components/TextInput.tsx b/front/src/modules/ui/input/components/TextInput.tsx
index f8d4490b0..f5d210b07 100644
--- a/front/src/modules/ui/input/components/TextInput.tsx
+++ b/front/src/modules/ui/input/components/TextInput.tsx
@@ -19,7 +19,7 @@ import { useCombinedRefs } from '~/hooks/useCombinedRefs';
import { InputHotkeyScope } from '../types/InputHotkeyScope';
-type TextInputComponentProps = Omit<
+export type TextInputComponentProps = Omit<
InputHTMLAttributes,
'onChange'
> & {
@@ -53,9 +53,10 @@ const StyledInputContainer = styled.div`
`;
const StyledInput = styled.input>`
- background-color: ${({ theme }) => theme.background.tertiary};
- border: none;
+ background-color: ${({ theme }) => theme.background.transparent.lighter};
+ border: 1px solid ${({ theme }) => theme.border.color.medium};
border-bottom-left-radius: ${({ theme }) => theme.border.radius.sm};
+ border-right: none;
border-top-left-radius: ${({ theme }) => theme.border.radius.sm};
color: ${({ theme }) => theme.font.color.primary};
display: flex;
@@ -74,6 +75,10 @@ const StyledInput = styled.input>`
font-family: ${({ theme }) => theme.font.family};
font-weight: ${({ theme }) => theme.font.weight.medium};
}
+
+ &:disabled {
+ color: ${({ theme }) => theme.font.color.tertiary};
+ }
`;
const StyledErrorHelper = styled.div`
@@ -84,8 +89,10 @@ const StyledErrorHelper = styled.div`
const StyledTrailingIconContainer = styled.div`
align-items: center;
- background-color: ${({ theme }) => theme.background.tertiary};
+ background-color: ${({ theme }) => theme.background.transparent.lighter};
+ border: 1px solid ${({ theme }) => theme.border.color.medium};
border-bottom-right-radius: ${({ theme }) => theme.border.radius.sm};
+ border-left: none;
border-top-right-radius: ${({ theme }) => theme.border.radius.sm};
display: flex;
justify-content: center;
diff --git a/front/src/modules/ui/input/components/__stories__/TextArea.stories.tsx b/front/src/modules/ui/input/components/__stories__/TextArea.stories.tsx
new file mode 100644
index 000000000..13f5a0655
--- /dev/null
+++ b/front/src/modules/ui/input/components/__stories__/TextArea.stories.tsx
@@ -0,0 +1,40 @@
+import { useState } from 'react';
+import { Meta, StoryObj } from '@storybook/react';
+
+import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
+
+import { TextArea, TextAreaProps } from '../TextArea';
+
+type RenderProps = TextAreaProps;
+
+const Render = (args: RenderProps) => {
+ const [value, setValue] = useState(args.value);
+ const handleChange = (text: string) => {
+ args.onChange?.(text);
+ setValue(text);
+ };
+
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ return ;
+};
+
+const meta: Meta = {
+ title: 'UI/Input/TextArea',
+ component: TextArea,
+ decorators: [ComponentDecorator],
+ args: { minRows: 4, placeholder: 'Lorem Ipsum' },
+ render: Render,
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {};
+
+export const Filled: Story = {
+ args: { value: 'Lorem Ipsum' },
+};
+
+export const Disabled: Story = {
+ args: { disabled: true, value: 'Lorem Ipsum' },
+};
diff --git a/front/src/modules/ui/input/components/__stories__/TextInput.stories.tsx b/front/src/modules/ui/input/components/__stories__/TextInput.stories.tsx
new file mode 100644
index 000000000..74dc2e96b
--- /dev/null
+++ b/front/src/modules/ui/input/components/__stories__/TextInput.stories.tsx
@@ -0,0 +1,40 @@
+import { useState } from 'react';
+import { Meta, StoryObj } from '@storybook/react';
+
+import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
+
+import { TextInput, TextInputComponentProps } from '../TextInput';
+
+type RenderProps = TextInputComponentProps;
+
+const Render = (args: RenderProps) => {
+ const [value, setValue] = useState(args.value);
+ const handleChange = (text: string) => {
+ args.onChange?.(text);
+ setValue(text);
+ };
+
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ return ;
+};
+
+const meta: Meta = {
+ title: 'UI/Input/TextInput',
+ component: TextInput,
+ decorators: [ComponentDecorator],
+ args: { placeholder: 'Tim' },
+ render: Render,
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {};
+
+export const Filled: Story = {
+ args: { value: 'Tim' },
+};
+
+export const Disabled: Story = {
+ args: { disabled: true, value: 'Tim' },
+};
diff --git a/front/src/pages/settings/data-model/SettingsNewObject.tsx b/front/src/pages/settings/data-model/SettingsNewObject.tsx
index 9259b735c..bc34e3aa9 100644
--- a/front/src/pages/settings/data-model/SettingsNewObject.tsx
+++ b/front/src/pages/settings/data-model/SettingsNewObject.tsx
@@ -1,9 +1,15 @@
+import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
-import { SettingsNewObjectType } from '@/settings/data-model/new-object/components/SettingsNewObjectType';
+import { SettingsObjectFormSection } from '@/settings/data-model/components/SettingsObjectFormSection';
+import {
+ NewObjectType,
+ SettingsNewObjectType,
+} from '@/settings/data-model/new-object/components/SettingsNewObjectType';
+import { SettingsObjectIconSection } from '@/settings/data-model/object-edit/SettingsObjectIconSection';
import { IconSettings } from '@/ui/display/icon';
import { H2Title } from '@/ui/display/typography/components/H2Title';
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
@@ -12,6 +18,22 @@ import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
export const SettingsNewObject = () => {
const navigate = useNavigate();
+ const [selectedObjectType, setSelectedObjectType] =
+ useState('Standard');
+
+ const [customFormValues, setCustomFormValues] = useState<
+ Partial<{
+ pluralName: string;
+ singularName: string;
+ description: string;
+ }>
+ >({});
+
+ const canSave =
+ selectedObjectType === 'Custom' &&
+ !!customFormValues.pluralName &&
+ !!customFormValues.singularName;
+
return (
@@ -23,11 +45,11 @@ export const SettingsNewObject = () => {
]}
/>
{
navigate('/settings/objects');
}}
- onSave={() => {}}
+ onSave={() => undefined}
/>
@@ -35,8 +57,27 @@ export const SettingsNewObject = () => {
title="Object Type"
description="The type of object you want to add"
/>
-
+
+ {selectedObjectType === 'Custom' && (
+ <>
+
+ {
+ setCustomFormValues((previousValues) => ({
+ ...previousValues,
+ ...formValues,
+ }));
+ }}
+ />
+ >
+ )}
);
diff --git a/front/src/pages/settings/data-model/SettingsObjectEdit.tsx b/front/src/pages/settings/data-model/SettingsObjectEdit.tsx
index af42346e3..765d2b192 100644
--- a/front/src/pages/settings/data-model/SettingsObjectEdit.tsx
+++ b/front/src/pages/settings/data-model/SettingsObjectEdit.tsx
@@ -40,10 +40,13 @@ export const SettingsObjectEdit = () => {
{activeObject && (
<>
{
>
)}
-
+