Skip to content

Commit

Permalink
Merge pull request #455 from Shelf-nu/436-feature-request-create-a-de…
Browse files Browse the repository at this point in the history
…fault-value-field-for-assets

created a default value field for assets
  • Loading branch information
DonKoko authored Nov 13, 2023
2 parents 02099f8 + b61e494 commit 0da1b65
Show file tree
Hide file tree
Showing 14 changed files with 216 additions and 25 deletions.
42 changes: 41 additions & 1 deletion app/components/assets/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ export const NewAssetFormSchema = z.object({
currentLocationId: z.string().optional(),
qrId: z.string().optional(),
tags: z.string().optional(),
valuation: z
.string()
.optional()
.transform((val) => (val ? +val : null)),
});

/** Pass props of the values to be used as default for the form fields */
Expand All @@ -41,6 +45,7 @@ interface Props {
category?: Asset["categoryId"];
location?: Asset["locationId"];
description?: Asset["description"];
valuation?: Asset["valuation"];
qrId?: Qr["id"] | null;
tags?: Tag[];
}
Expand All @@ -49,6 +54,7 @@ export const AssetForm = ({
title,
category,
description,
valuation,
qrId,
tags,
}: Props) => {
Expand Down Expand Up @@ -79,6 +85,8 @@ export const AssetForm = ({
const [, validateFile] = useAtom(validateFileAtom);
const [, updateDynamicTitle] = useAtom(updateDynamicTitleAtom);

const { currency } = useLoaderData<typeof loader>();

return (
<Form
ref={zo.ref}
Expand Down Expand Up @@ -168,12 +176,44 @@ export const AssetForm = ({
</Link>
</p>
}
className="pt-[10px]"
className="border-b-0 py-[10px]"
required={zodFieldIsRequired(FormSchema.shape.newLocationId)}
>
<LocationSelect />
</FormRow>

<FormRow
rowLabel={"Value"}
subHeading={
<p>
Specify the value of assets to get an idea of the total value of
your inventory.
</p>
}
className="border-b-0 py-[10px]"
required={zodFieldIsRequired(FormSchema.shape.valuation)}
>
<div className="relative w-full">
<Input
type="number"
label="value"
inputClassName="pl-[70px] valuation-input"
hideLabel
name={zo.fields.valuation()}
disabled={disabled}
error={zo.errors.valuation()?.message}
step="any"
min={0}
className="w-full"
defaultValue={valuation || ""}
required={zodFieldIsRequired(FormSchema.shape.valuation)}
/>
<span className="absolute bottom-0 border-r px-3 py-2.5 text-[16px] text-gray-600 lg:bottom-[11px]">
{currency}
</span>
</div>
</FormRow>

<div>
<FormRow
rowLabel={"Description"}
Expand Down
51 changes: 47 additions & 4 deletions app/components/workspace/form.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type CustomField } from "@prisma/client";
import type { Organization, $Enums } from "@prisma/client";
import { Currency } from "@prisma/client";
import { Form, useNavigation } from "@remix-run/react";
import { useAtom, useAtomValue } from "jotai";
import { useZorm } from "react-zorm";
Expand All @@ -9,19 +10,28 @@ import { isFormProcessing } from "~/utils";
import { zodFieldIsRequired } from "~/utils/zod";
import FormRow from "../forms/form-row";
import Input from "../forms/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "../forms/select";
import { Button } from "../shared";
import { Spinner } from "../shared/spinner";

export const NewWorkspaceFormSchema = z.object({
name: z.string().min(2, "Name is required"),
currency: z.custom<Currency>(),
});

/** Pass props of the values to be used as default for the form fields */
interface Props {
name?: CustomField["name"];
name?: Organization["name"];
currency?: Organization["currency"];
}

export const WorkspaceForm = ({ name }: Props) => {
export const WorkspaceForm = ({ name, currency }: Props) => {
const navigation = useNavigation();
const zo = useZorm("NewQuestionWizardScreen", NewWorkspaceFormSchema);
const disabled = isFormProcessing(navigation.state);
Expand Down Expand Up @@ -56,7 +66,7 @@ export const WorkspaceForm = ({ name }: Props) => {
/>
</FormRow>

<FormRow rowLabel={"Main image"}>
<FormRow rowLabel={"Main image"} className="border-b-0">
<div>
<p className="hidden lg:block">Accepts PNG, JPG or JPEG (max.4 MB)</p>
<Input
Expand All @@ -75,6 +85,39 @@ export const WorkspaceForm = ({ name }: Props) => {
</div>
</FormRow>

<div>
<label className="lg:hidden">Currency</label>
<FormRow rowLabel={"Currency"}>
<Select
defaultValue={currency || "USD"}
disabled={disabled}
name={zo.fields.currency()}
>
<SelectTrigger
className="px-3.5 py-3"
placeholder="Choose a field type"
>
<SelectValue />
</SelectTrigger>
<SelectContent
position="popper"
className="w-full min-w-[300px]"
align="start"
>
<div className=" max-h-[320px] overflow-auto">
{Object.keys(Currency).map((value) => (
<SelectItem value={value} key={value}>
<span className="mr-4 text-[14px] text-gray-700">
{Currency[value as $Enums.Currency]}
</span>
</SelectItem>
))}
</div>
</SelectContent>
</Select>
</FormRow>
</div>

<div className="text-right">
<Button type="submit" disabled={disabled}>
{disabled ? <Spinner /> : "Save"}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- CreateEnum
CREATE TYPE "Currency" AS ENUM ('USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD', 'CHF', 'CNY', 'INR', 'ZAR', 'BRL', 'MXN', 'SGD', 'NZD', 'SEK', 'NOK', 'KRW', 'RUB', 'HKD', 'SAR');

-- AlterTable
ALTER TABLE "Asset" ADD COLUMN "value" DOUBLE PRECISION;

-- AlterTable
ALTER TABLE "Organization" ADD COLUMN "currency" "Currency" NOT NULL DEFAULT 'USD';
42 changes: 35 additions & 7 deletions app/database/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ model Asset {
mainImage String?
mainImageExpiration DateTime?
status AssetStatus @default(AVAILABLE)
valuation Float? @map("value") // Field to store the monetary value of an asset
// Datetime
createdAt DateTime @default(now())
Expand Down Expand Up @@ -288,8 +289,8 @@ model TeamMember {
user User? @relation(fields: [userId], references: [id], onUpdate: Cascade)
userId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
}

Expand All @@ -312,14 +313,18 @@ model Organization {
name String @default("Personal")
type OrganizationType @default(PERSONAL)
owner User @relation(fields: [userId], references: [id], onUpdate: Cascade)
userId String
owner User @relation(fields: [userId], references: [id], onUpdate: Cascade)
userId String
currency Currency @default(USD)
members TeamMember[]
assets Asset[]
image Image? @relation("orgImage", fields: [imageId], references: [id])
imageId String? @unique
members TeamMember[]
assets Asset[]
// members TeamMember[]
// assets Asset[]
locations Location[]
categories Category[]
tags Tag[]
Expand All @@ -330,7 +335,7 @@ model Organization {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
customFields CustomField[]
images Image[] @relation("owner")
images Image[] @relation("owner")
}

model UserOrganization {
Expand Down Expand Up @@ -439,6 +444,29 @@ model AssetCustomFieldValue {
updatedAt DateTime @updatedAt
}

enum Currency {
USD // United States Dollar
EUR // Euro
GBP // British Pound Sterling
JPY // Japanese Yen
AUD // Australian Dollar
CAD // Canadian Dollar
CHF // Swiss Franc
CNY // Chinese Yuan
INR // Indian Rupee
ZAR // South African Rand
BRL // Brazilian Real
MXN // Mexican Peso
SGD // Singapore Dollar
NZD // New Zealand Dollar
SEK // Swedish Krona
NOK // Norwegian Krone
KRW // South Korean Won
RUB // Russian Ruble
HKD // Hong Kong Dollar
SAR // Saudi Riyal
}

enum InviteStatuses {
PENDING
ACCEPTED
Expand Down
19 changes: 17 additions & 2 deletions app/modules/asset/service.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ export async function getAsset({
custodian: true,
},
},
organization: {
select: {
currency: true,
},
},
customFields: {
where: {
customField: {
Expand Down Expand Up @@ -224,7 +229,11 @@ export async function createAsset({
custodian,
customFieldsValues,
organizationId,
}: Pick<Asset, "description" | "title" | "categoryId" | "userId"> & {
valuation,
}: Pick<
Asset,
"description" | "title" | "categoryId" | "userId" | "valuation"
> & {
qrId?: Qr["id"];
locationId?: Location["id"];
tags?: { set: { id: string }[] };
Expand Down Expand Up @@ -274,6 +283,7 @@ export async function createAsset({
description,
user,
qrCodes,
valuation,
organization,
};

Expand Down Expand Up @@ -363,14 +373,16 @@ export async function updateAsset(payload: UpdateAssetPayload) {
id,
newLocationId,
currentLocationId,
customFieldsValues: customFieldsValuesFromForm,
userId,
valuation,
customFieldsValues: customFieldsValuesFromForm,
} = payload;
const isChangingLocation = newLocationId !== currentLocationId;

const data = {
title,
description,
valuation,
mainImage,
mainImageExpiration,
};
Expand Down Expand Up @@ -653,6 +665,7 @@ export async function duplicateAsset({
locationId: asset.locationId ?? undefined,
custodian: asset?.custody?.custodian.id ?? undefined,
tags: { set: asset.tags.map((tag) => ({ id: tag.id })) },
valuation: asset.valuation,
});

if (asset.mainImage) {
Expand Down Expand Up @@ -905,6 +918,7 @@ export const createAssetsFromContentImport = async ({
.map((t) => ({ id: tags[t] })),
}
: undefined,
valuation: asset.valuation ? +asset.valuation : null,
customFieldsValues,
});
}
Expand Down Expand Up @@ -943,6 +957,7 @@ export const createAssetsFromBackupImport = async ({
},
],
},
valuation: asset.valuation ? +asset.valuation : null,
},
};

Expand Down
1 change: 1 addition & 0 deletions app/modules/asset/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface UpdateAssetPayload {
tags?: { set: { id: string }[] };
userId: User["id"];
customFieldsValues?: ShelfAssetCustomFieldValueType[];
valuation?: Asset["valuation"];
}

export interface CreateAssetFromContentImportPayload
Expand Down
9 changes: 7 additions & 2 deletions app/modules/organization/service.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const getOrganizationByUserId = async ({
id: true,
name: true,
type: true,
currency: true,
},
});

Expand Down Expand Up @@ -63,12 +64,14 @@ export async function createOrganization({
name,
userId,
image,
}: Pick<Organization, "name"> & {
currency,
}: Pick<Organization, "name" | "currency"> & {
userId: User["id"];
image: File | null;
}) {
const data = {
name,
currency,
type: OrganizationType.TEAM,
categories: {
create: defaultUserCategories.map((c) => ({ ...c, userId })),
Expand Down Expand Up @@ -117,12 +120,14 @@ export async function updateOrganization({
name,
image,
userId,
}: Pick<Organization, "name" | "id"> & {
currency,
}: Pick<Organization, "name" | "id" | "currency"> & {
userId: User["id"];
image: File | null;
}) {
const data = {
name,
currency,
};

if (image?.size && image?.size > 0) {
Expand Down
Loading

0 comments on commit 0da1b65

Please sign in to comment.