+ );
+};
+
+export default GeneralDetails;
diff --git a/packages/webapp/src/components/Animals/AddAnimalsDetails/Origin.tsx b/packages/webapp/src/components/Animals/AddAnimalsDetails/Origin.tsx
new file mode 100644
index 0000000000..3fb8ebb98e
--- /dev/null
+++ b/packages/webapp/src/components/Animals/AddAnimalsDetails/Origin.tsx
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2024 LiteFarm.org
+ * This file is part of LiteFarm.
+ *
+ * LiteFarm is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LiteFarm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details, see .
+ */
+
+import { useMemo } from 'react';
+import { useFormContext } from 'react-hook-form';
+import Input, { getInputErrors } from '../../Form/Input';
+import RadioGroup from '../../Form/RadioGroup';
+import { DetailsFields, type Option, type CommonDetailsProps } from './type';
+import { AnimalOrigins } from '../../../containers/Animals/types';
+import styles from './styles.module.scss';
+
+export type OriginProps = CommonDetailsProps & {
+ currency: string;
+ originOptions: Option[DetailsFields.ORIGIN][];
+ origin?: AnimalOrigins;
+};
+
+const Origin = ({ t, currency, originOptions, origin }: OriginProps) => {
+ const {
+ control,
+ register,
+ trigger,
+ formState: { errors },
+ } = useFormContext();
+
+ const fields = useMemo(() => {
+ return origin === AnimalOrigins.BROUGHT_IN ? (
+ <>
+ {/* @ts-ignore */}
+
+ {/* @ts-ignore */}
+
+ {/* @ts-ignore */}
+
+ >
+ ) : (
+ <>
+ {/* @ts-ignore */}
+
+ {/* @ts-ignore */}
+
+ >
+ );
+ }, [origin, Object.entries(errors)]);
+
+ return (
+
+
+ {/* @ts-ignore */}
+
+
+ {origin && fields}
+
+ );
+};
+
+export default Origin;
diff --git a/packages/webapp/src/components/Animals/AddAnimalsDetails/Other.tsx b/packages/webapp/src/components/Animals/AddAnimalsDetails/Other.tsx
new file mode 100644
index 0000000000..7094936216
--- /dev/null
+++ b/packages/webapp/src/components/Animals/AddAnimalsDetails/Other.tsx
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2024 LiteFarm.org
+ * This file is part of LiteFarm.
+ *
+ * LiteFarm is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LiteFarm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details, see .
+ */
+
+import { Controller, useController, useFormContext } from 'react-hook-form';
+import ReactSelect from '../../Form/ReactSelect';
+import Input from '../../Form/Input';
+import InputAutoSize from '../../Form/InputAutoSize';
+import ImagePicker from '../../ImagePicker';
+import { AnimalOrBatchKeys } from '../../../containers/Animals/types';
+import styles from './styles.module.scss';
+import { DetailsFields, type Option, type CommonDetailsProps } from './type';
+
+export type OtherDetailsProps = CommonDetailsProps & {
+ organicStatusOptions: Option[DetailsFields.ORGANIC_STATUS][];
+ animalOrBatch: AnimalOrBatchKeys;
+};
+
+const OtherDetails = ({ t, organicStatusOptions, animalOrBatch }: OtherDetailsProps) => {
+ const {
+ control,
+ resetField,
+ register,
+ formState: { errors },
+ } = useFormContext();
+
+ const { field } = useController({ control, name: DetailsFields.ANIMAL_IMAGE });
+
+ const handleSelectImage = (imageFile: any) => {
+ field.onChange(imageFile);
+ };
+
+ const handleRemoveImage = () => {
+ resetField(DetailsFields.ANIMAL_IMAGE);
+ };
+
+ return (
+
+ );
+};
+
+export default OtherDetails;
diff --git a/packages/webapp/src/components/Animals/AddAnimalsDetails/Unique.tsx b/packages/webapp/src/components/Animals/AddAnimalsDetails/Unique.tsx
new file mode 100644
index 0000000000..fe09843019
--- /dev/null
+++ b/packages/webapp/src/components/Animals/AddAnimalsDetails/Unique.tsx
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2024 LiteFarm.org
+ * This file is part of LiteFarm.
+ *
+ * LiteFarm is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LiteFarm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details, see .
+ */
+
+import { Controller, useFormContext } from 'react-hook-form';
+import ReactSelect from '../../Form/ReactSelect';
+import Input, { getInputErrors } from '../../Form/Input';
+import { DetailsFields, type Option, type CommonDetailsProps } from './type';
+import styles from './styles.module.scss';
+
+export type UniqueDetailsProps = CommonDetailsProps & {
+ tagTypeOptions: Option[DetailsFields.TAG_TYPE][];
+ tagColorOptions: Option[DetailsFields.TAG_COLOR][];
+ tagPlacementOptions: Option[DetailsFields.TAG_PLACEMENT][];
+ shouldShowTagTypeInput?: boolean;
+ shouldShowTagPlacementInput?: boolean;
+};
+
+const UniqueDetails = ({
+ t,
+ tagTypeOptions,
+ tagColorOptions,
+ tagPlacementOptions,
+ shouldShowTagTypeInput,
+ shouldShowTagPlacementInput,
+}: UniqueDetailsProps) => {
+ const {
+ control,
+ register,
+ trigger,
+ formState: { errors },
+ } = useFormContext();
+
+ return (
+
+ );
+};
+
+export default UniqueDetails;
diff --git a/packages/webapp/src/components/Animals/AddAnimalsDetails/index.tsx b/packages/webapp/src/components/Animals/AddAnimalsDetails/index.tsx
new file mode 100644
index 0000000000..dfe5b2d6e3
--- /dev/null
+++ b/packages/webapp/src/components/Animals/AddAnimalsDetails/index.tsx
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2024 LiteFarm.org
+ * This file is part of LiteFarm.
+ *
+ * LiteFarm is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LiteFarm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details, see .
+ */
+
+import { useTranslation } from 'react-i18next';
+import clsx from 'clsx';
+import GeneralDetails, { type GeneralDetailsProps } from './General';
+import UniqueDetails, { type UniqueDetailsProps } from './Unique';
+import OtherDetails, { type OtherDetailsProps } from './Other';
+import Origin, { type OriginProps } from './Origin';
+import ExpandableItem from '../../Expandable/ExpandableItem';
+import useExpandable from '../../Expandable/useExpandableItem';
+import { AnimalOrBatchKeys } from '../../../containers/Animals/types';
+import styles from './styles.module.scss';
+
+enum sectionKeys {
+ GENERAL,
+ ORIGIN,
+ UNIQUE,
+ OTHER,
+}
+
+export type AnimalDetailsProps = {
+ generalDetailProps: Omit;
+ uniqueDetailsProps: Omit;
+ otherDetailsProps: Omit;
+ originProps: Omit;
+};
+
+const AnimalDetails = ({
+ generalDetailProps,
+ uniqueDetailsProps,
+ otherDetailsProps,
+ originProps,
+}: AnimalDetailsProps) => {
+ const { expandedIds, toggleExpanded } = useExpandable({ isSingleExpandable: true });
+ const { t } = useTranslation(['translation', 'common', 'animal']);
+ const commonProps = { t };
+
+ const sections = [
+ {
+ key: sectionKeys.GENERAL,
+ title: t('ADD_ANIMAL.GENERAL_DETAILS'),
+ content: (
+
+ ),
+ },
+ {
+ key: sectionKeys.UNIQUE,
+ title: t('ADD_ANIMAL.UNIQUE_DETAILS'),
+ content: ,
+ },
+ {
+ key: sectionKeys.OTHER,
+ title: t('ADD_ANIMAL.OTHER_DETAILS'),
+ content: (
+
+ ),
+ },
+ {
+ key: sectionKeys.ORIGIN,
+ title: t('ADD_ANIMAL.ORIGIN'),
+ content: ,
+ },
+ ];
+
+ return (
+
+ );
+}
diff --git a/packages/webapp/src/components/ImagePicker/styles.module.scss b/packages/webapp/src/components/ImagePicker/styles.module.scss
new file mode 100644
index 0000000000..a3fcd228e8
--- /dev/null
+++ b/packages/webapp/src/components/ImagePicker/styles.module.scss
@@ -0,0 +1,89 @@
+/*
+* Copyright 2024 LiteFarm.org
+* This file is part of LiteFarm.
+*
+* LiteFarm is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* LiteFarm is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details, see .
+*/
+
+.imageContainer {
+ display: flex;
+ align-items: start;
+
+ img {
+ max-width: 150px;
+ max-height: 75px;
+ object-fit: cover;
+ }
+}
+
+.imageActions {
+ margin-left: 8px;
+ gap: 4px;
+
+ button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 4px;
+ color: var(--teal700);
+ font-weight: 400;
+ }
+}
+
+.filePickerBtn {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-weight: 700;
+ color: #634d00;
+ border: 1px solid var(--grey600);
+ border-radius: 4px;
+ padding: 12px 24px;
+}
+
+.filePickerWrapper {
+ @media (min-width: 768px) {
+ display: none;
+ }
+}
+
+.dropContainer {
+ display: none;
+ height: 145px;
+ background-color: var(--grey100);
+ border: 1px dashed var(--grey500);
+ border-radius: 4px;
+
+ .cameraIcon {
+ width: 40px;
+ height: 40px;
+ path {
+ stroke: #abc7c1;
+ }
+ }
+
+ .flexWrapper {
+ display: flex;
+ gap: 4px;
+ }
+
+ @media (min-width: 768px) {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ }
+}
+
+.dropContainerActive {
+ background-color: var(--teal100);
+ border: 1px solid var(--teal500);
+}
diff --git a/packages/webapp/src/containers/Animals/types.ts b/packages/webapp/src/containers/Animals/types.ts
index b5baa52270..fa922e9030 100644
--- a/packages/webapp/src/containers/Animals/types.ts
+++ b/packages/webapp/src/containers/Animals/types.ts
@@ -28,3 +28,13 @@ export enum AnimalOrBatchKeys {
ANIMAL = 'ANIMAL',
BATCH = 'BATCH',
}
+
+export enum AnimalSexes {
+ MALE = 'MALE',
+ FEMALE = 'FEMALE',
+}
+
+export enum AnimalOrigins {
+ BROUGHT_IN = 'BROUGHT_IN',
+ BORN_AT_FARM = 'BORN_AT_FARM',
+}
diff --git a/packages/webapp/src/stories/Animals/Details/AnimalDetails.stories.tsx b/packages/webapp/src/stories/Animals/Details/AnimalDetails.stories.tsx
new file mode 100644
index 0000000000..170d6e7d2c
--- /dev/null
+++ b/packages/webapp/src/stories/Animals/Details/AnimalDetails.stories.tsx
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2024 LiteFarm.org
+ * This file is part of LiteFarm.
+ *
+ * LiteFarm is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LiteFarm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details, see .
+ */
+
+import { Suspense } from 'react';
+import { FormProvider, useForm } from 'react-hook-form';
+import { Meta, StoryObj } from '@storybook/react';
+import AnimalCreationDetails, {
+ AnimalDetailsProps,
+} from '../../../components/Animals/AddAnimalsDetails';
+import AnimalDetails from '../../../components/Animals/AddAnimalsDetails';
+import { FormMethods } from '../../../components/Animals/AddAnimalsDetails/type';
+import {
+ typeOptions,
+ breedOptions,
+ sexOptions,
+ useOptions,
+ tagTypeOptions,
+ tagColorOptions,
+ tagPlacementOptions,
+ organicStatusOptions,
+ originOptions,
+} from './mockData';
+
+// https://storybook.js.org/docs/writing-stories/typescript
+const meta: Meta = {
+ title: 'Components/AddAnimalsDetails',
+ component: AnimalDetails,
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: () => {
+ const formMethods: FormMethods = useForm();
+
+ return (
+
+
+
+
+
+
+
+ );
+ },
+};
diff --git a/packages/webapp/src/stories/Animals/Details/General.stories.tsx b/packages/webapp/src/stories/Animals/Details/General.stories.tsx
new file mode 100644
index 0000000000..f6c465f683
--- /dev/null
+++ b/packages/webapp/src/stories/Animals/Details/General.stories.tsx
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2024 LiteFarm.org
+ * This file is part of LiteFarm.
+ *
+ * LiteFarm is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LiteFarm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details, see .
+ */
+
+import { FormProvider, useForm } from 'react-hook-form';
+import { useTranslation } from 'react-i18next';
+import { Meta, StoryObj } from '@storybook/react';
+import { componentDecorators } from '../../Pages/config/Decorators';
+import GeneralDetails, {
+ GeneralDetailsProps,
+} from '../../../components/Animals/AddAnimalsDetails/General';
+import { AnimalOrBatchKeys } from '../../../containers/Animals/types';
+import { DetailsFields, FormMethods } from '../../../components/Animals/AddAnimalsDetails/type';
+import { typeOptions, breedOptions, sexOptions, useOptions } from './mockData';
+
+// https://storybook.js.org/docs/writing-stories/typescript
+const meta: Meta = {
+ title: 'Components/AddAnimalsDetails/General',
+ component: GeneralDetails,
+ decorators: [
+ ...componentDecorators,
+ (Story) => {
+ const { t } = useTranslation();
+ const formMethods: FormMethods = useForm({ mode: 'onBlur' });
+ const sex = formMethods.watch(DetailsFields.SEX);
+ const isMaleSelected = sex === 1;
+
+ return (
+
+
+
+ );
+ },
+ ],
+ // avoid "Maximum update depth exceeded" https://github.com/storybookjs/storybook/issues/12306
+ parameters: { docs: { source: { type: 'code' } } },
+};
+export default meta;
+
+type Story = StoryObj;
+
+const commonProps = { typeOptions, breedOptions, sexOptions, useOptions };
+
+export const Animal: Story = {
+ args: {
+ ...commonProps,
+ animalOrBatch: AnimalOrBatchKeys.ANIMAL,
+ },
+ render: (args, context) => ,
+};
+
+export const Batch: Story = {
+ args: {
+ ...commonProps,
+ animalOrBatch: AnimalOrBatchKeys.BATCH,
+ },
+ render: (args, context) => ,
+};
diff --git a/packages/webapp/src/stories/Animals/Details/Origin.stories.tsx b/packages/webapp/src/stories/Animals/Details/Origin.stories.tsx
new file mode 100644
index 0000000000..4fe21f5e9e
--- /dev/null
+++ b/packages/webapp/src/stories/Animals/Details/Origin.stories.tsx
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024 LiteFarm.org
+ * This file is part of LiteFarm.
+ *
+ * LiteFarm is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LiteFarm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details, see .
+ */
+
+import { FormProvider, useForm } from 'react-hook-form';
+import { useTranslation } from 'react-i18next';
+import { Meta, StoryObj } from '@storybook/react';
+import { componentDecorators } from '../../Pages/config/Decorators';
+import Origin, { OriginProps } from '../../../components/Animals/AddAnimalsDetails/Origin';
+import { AnimalOrigins } from '../../../containers/Animals/types';
+import { DetailsFields, FormMethods } from '../../../components/Animals/AddAnimalsDetails/type';
+import { originOptions } from './mockData';
+
+// https://storybook.js.org/docs/writing-stories/typescript
+const meta: Meta = {
+ title: 'Components/AddAnimalsDetails/Origin',
+ component: Origin,
+ decorators: [
+ ...componentDecorators,
+ (Story) => {
+ const { t } = useTranslation();
+ const formMethods: FormMethods = useForm({ mode: 'onBlur' });
+ const originId = formMethods.watch(DetailsFields.ORIGIN);
+ const origin = !originId
+ ? undefined
+ : originId === 1
+ ? AnimalOrigins.BROUGHT_IN
+ : AnimalOrigins.BORN_AT_FARM;
+
+ return (
+
+
+
+ );
+ },
+ ],
+ // avoid "Maximum update depth exceeded" https://github.com/storybookjs/storybook/issues/12306
+ parameters: { docs: { source: { type: 'code' } } },
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const OriginDetails: Story = {
+ args: { currency: '$', originOptions },
+ render: (args, context) => ,
+};
diff --git a/packages/webapp/src/stories/Animals/Details/Other.stories.tsx b/packages/webapp/src/stories/Animals/Details/Other.stories.tsx
new file mode 100644
index 0000000000..bdb31df60c
--- /dev/null
+++ b/packages/webapp/src/stories/Animals/Details/Other.stories.tsx
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2024 LiteFarm.org
+ * This file is part of LiteFarm.
+ *
+ * LiteFarm is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LiteFarm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details, see .
+ */
+
+import { FormProvider, useForm } from 'react-hook-form';
+import { useTranslation } from 'react-i18next';
+import { Meta, StoryObj } from '@storybook/react';
+import { componentDecorators } from '../../Pages/config/Decorators';
+import Other, { OtherDetailsProps } from '../../../components/Animals/AddAnimalsDetails/Other';
+import { AnimalOrBatchKeys } from '../../../containers/Animals/types';
+import { FormMethods } from '../../../components/Animals/AddAnimalsDetails/type';
+import { organicStatusOptions } from './mockData';
+
+// https://storybook.js.org/docs/writing-stories/typescript
+const meta: Meta = {
+ title: 'Components/AddAnimalsDetails/Other',
+ component: Other,
+ decorators: [
+ ...componentDecorators,
+ (Story) => {
+ const { t } = useTranslation();
+ const formMethods: FormMethods = useForm({ mode: 'onBlur' });
+
+ return (
+
+
+
+ );
+ },
+ ],
+ // avoid "Maximum update depth exceeded" https://github.com/storybookjs/storybook/issues/12306
+ parameters: { docs: { source: { type: 'code' } } },
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Animal: Story = {
+ args: {
+ organicStatusOptions,
+ animalOrBatch: AnimalOrBatchKeys.ANIMAL,
+ },
+ render: (args, context) => ,
+};
+
+export const Batch: Story = {
+ args: {
+ organicStatusOptions,
+ animalOrBatch: AnimalOrBatchKeys.BATCH,
+ },
+ render: (args, context) => ,
+};
diff --git a/packages/webapp/src/stories/Animals/Details/Unique.stories.tsx b/packages/webapp/src/stories/Animals/Details/Unique.stories.tsx
new file mode 100644
index 0000000000..9b72235992
--- /dev/null
+++ b/packages/webapp/src/stories/Animals/Details/Unique.stories.tsx
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024 LiteFarm.org
+ * This file is part of LiteFarm.
+ *
+ * LiteFarm is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LiteFarm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details, see .
+ */
+
+import { FormProvider, useForm } from 'react-hook-form';
+import { useTranslation } from 'react-i18next';
+import { Meta, StoryObj } from '@storybook/react';
+import { componentDecorators } from '../../Pages/config/Decorators';
+import UniqueDetails, {
+ UniqueDetailsProps,
+} from '../../../components/Animals/AddAnimalsDetails/Unique';
+import { DetailsFields, FormMethods } from '../../../components/Animals/AddAnimalsDetails/type';
+import { tagTypeOptions, tagColorOptions, tagPlacementOptions } from './mockData';
+
+// https://storybook.js.org/docs/writing-stories/typescript
+const meta: Meta = {
+ title: 'Components/AddAnimalsDetails/Unique',
+ component: UniqueDetails,
+ decorators: [
+ ...componentDecorators,
+ (Story) => {
+ const { t } = useTranslation();
+ const formMethods: FormMethods = useForm({ mode: 'onBlur' });
+ const tagType = formMethods.watch(DetailsFields.TAG_TYPE);
+ const tagPlacement = formMethods.watch(DetailsFields.TAG_PLACEMENT);
+ const shouldShowTagTypeInput = tagType?.label === 'Other';
+ const shouldShowTagPlacementInput = tagPlacement?.label === 'Other';
+
+ return (
+
+
+
+ );
+ },
+ ],
+ // avoid "Maximum update depth exceeded" https://github.com/storybookjs/storybook/issues/12306
+ parameters: { docs: { source: { type: 'code' } } },
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Unique: Story = {
+ args: { tagTypeOptions, tagColorOptions, tagPlacementOptions },
+ render: (args, context) => ,
+};
diff --git a/packages/webapp/src/stories/Animals/Details/mockData.js b/packages/webapp/src/stories/Animals/Details/mockData.js
new file mode 100644
index 0000000000..76b1e19622
--- /dev/null
+++ b/packages/webapp/src/stories/Animals/Details/mockData.js
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2024 LiteFarm.org
+ * This file is part of LiteFarm.
+ *
+ * LiteFarm is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LiteFarm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details, see .
+ */
+
+export const typeOptions = [
+ { value: 'default_1', label: 'Cattle' },
+ { value: 'default_2', label: 'Pig' },
+ { value: 'default_3', label: 'Chicken' },
+ { value: 'custom_1', label: 'Sheep' },
+];
+
+export const breedOptions = [
+ { value: '1', label: 'Angus' },
+ { value: '2', label: 'Cobb 5' },
+];
+
+export const sexOptions = [
+ { value: 0, label: `I don't know` },
+ { value: 1, label: 'Male' },
+ { value: 2, label: 'Female' },
+];
+
+export const useOptions = [
+ { label: 'A', value: 'A' },
+ { label: 'B', value: 'B' },
+ { label: 'C', value: 'C' },
+];
+
+export const tagTypeOptions = [
+ { value: 1, label: 'Ear tags' },
+ { value: 2, label: 'Leg bands' },
+ { value: 3, label: 'Other' },
+];
+
+export const tagColorOptions = [
+ { value: 1, label: 'Yellow' },
+ { value: 2, label: 'White' },
+ { value: 3, label: 'Orange' },
+ { value: 4, label: 'Green' },
+ { value: 5, label: 'Blue' },
+ { value: 6, label: 'Red' },
+];
+
+export const tagPlacementOptions = [
+ { value: 1, label: 'Left ear' },
+ { value: 2, label: 'Right ear' },
+ { value: 3, label: 'Left leg' },
+ { value: 4, label: 'Right leg' },
+ { value: 5, label: 'Other' },
+];
+
+export const organicStatusOptions = [
+ { value: 1, label: 'Non-Organic' },
+ { value: 2, label: 'Organic' },
+ { value: 3, label: 'Transitioning' },
+];
+
+export const originOptions = [
+ { value: 1, label: 'Brought in' },
+ { value: 2, label: 'Born at the farm' },
+];
diff --git a/packages/webapp/src/stories/ImagePicker/ImagePicker.stories.tsx b/packages/webapp/src/stories/ImagePicker/ImagePicker.stories.tsx
new file mode 100644
index 0000000000..50c25b8d1a
--- /dev/null
+++ b/packages/webapp/src/stories/ImagePicker/ImagePicker.stories.tsx
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024 LiteFarm.org
+ * This file is part of LiteFarm.
+ *
+ * LiteFarm is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LiteFarm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details, see .
+ */
+
+import { Meta, StoryObj } from '@storybook/react';
+import { componentDecorators } from '../Pages/config/Decorators';
+import ImagePicker, { type ImagePickerProps } from '../../components/ImagePicker';
+
+// https://storybook.js.org/docs/writing-stories/typescript
+const meta: Meta = {
+ title: 'Components/ImagePicker',
+ component: ImagePicker,
+ decorators: componentDecorators,
+ args: {
+ label: 'Image',
+ onSelectImage: () => console.log('select'),
+ onRemoveImage: () => console.log('remove'),
+ },
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {};
+
+export const WithDefaultUrl: Story = {
+ args: {
+ defaultUrl: '/src/assets/images/certification/Farmland.svg',
+ },
+};