mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-20 14:54:15 +01:00
(feat) prevent show product in store if price not set
This commit is contained in:
parent
82d2f99b77
commit
f61e784ace
@ -1,4 +1,5 @@
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
import ParsingLib from '../../lib/parsing';
|
||||
|
||||
type Error = { error: string };
|
||||
|
||||
@ -48,7 +49,9 @@ function extractHumanReadableMessage (error: string|Error): string {
|
||||
// iterate through all the keys to build the message
|
||||
for (const key in error) {
|
||||
if (Object.prototype.hasOwnProperty.call(error, key)) {
|
||||
message += `${key} : `;
|
||||
if (!ParsingLib.isInteger(key)) {
|
||||
message += `${key} : `;
|
||||
}
|
||||
if (error[key] instanceof Array) {
|
||||
// standard rails messages are stored as {field: [error1, error2]}
|
||||
// we rebuild them as "field: error1, error2"
|
||||
|
@ -59,7 +59,7 @@ export const FormInput = <TFieldValues extends FieldValues, TInputType>({ id, re
|
||||
{...register(id as FieldPath<TFieldValues>, {
|
||||
...rules,
|
||||
valueAsDate: type === 'date',
|
||||
setValueAs: v => (v === null && nullable) ? null : (type === 'number' ? parseFloat(v) : v),
|
||||
setValueAs: v => ([null, ''].includes(v) && nullable) ? null : (type === 'number' ? parseFloat(v) : v),
|
||||
value: defaultValue as FieldPathValue<TFieldValues, FieldPath<TFieldValues>>,
|
||||
onChange: (e) => { handleChange(e); }
|
||||
})}
|
||||
|
@ -50,7 +50,7 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
|
||||
|
||||
const { handleSubmit, register, control, formState, setValue, reset } = useForm<Product>({ defaultValues: { ...product } });
|
||||
const output = useWatch<Product>({ control });
|
||||
const [isActivePrice, setIsActivePrice] = useState<boolean>(product.id && _.isFinite(product.amount) && product.amount > 0);
|
||||
const [isActivePrice, setIsActivePrice] = useState<boolean>(product.id && _.isFinite(product.amount));
|
||||
const [productCategories, setProductCategories] = useState<selectOption[]>([]);
|
||||
const [machines, setMachines] = useState<checklistOption[]>([]);
|
||||
const [stockTab, setStockTab] = useState<boolean>(false);
|
||||
@ -91,12 +91,23 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
|
||||
setValue('slug', slug);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when the user toggles the visibility of the product in the store.
|
||||
*/
|
||||
const handleIsActiveChanged = (value: boolean): void => {
|
||||
if (value) {
|
||||
setValue('is_active_price', true);
|
||||
setIsActivePrice(true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when is active price has changed.
|
||||
*/
|
||||
const toggleIsActivePrice = (value: boolean) => {
|
||||
if (!value) {
|
||||
setValue('amount', null);
|
||||
setValue('is_active', false);
|
||||
}
|
||||
setIsActivePrice(value);
|
||||
};
|
||||
@ -262,6 +273,7 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
|
||||
formState={formState}
|
||||
label={t('app.admin.store.product_form.is_show_in_store')}
|
||||
tooltip={t('app.admin.store.product_form.active_price_info')}
|
||||
onChange={handleIsActiveChanged}
|
||||
className='span-3' />
|
||||
</div>
|
||||
|
||||
@ -280,10 +292,11 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
|
||||
<FormInput id="amount"
|
||||
type="number"
|
||||
register={register}
|
||||
rules={{ required: true, min: 0 }}
|
||||
rules={{ required: isActivePrice, min: 0 }}
|
||||
step={0.01}
|
||||
formState={formState}
|
||||
label={t('app.admin.store.product_form.price')} />
|
||||
label={t('app.admin.store.product_form.price')}
|
||||
nullable />
|
||||
<FormInput id="quantity_min"
|
||||
type="number"
|
||||
rules={{ required: true }}
|
||||
|
@ -14,11 +14,25 @@ export default class ParsingLib {
|
||||
for (const item of value) {
|
||||
parsedValue.push(ParsingLib.parse(item));
|
||||
}
|
||||
} else if (['true', 'false'].includes(value)) {
|
||||
} else if (ParsingLib.isBoolean(value)) {
|
||||
parsedValue = (value === 'true');
|
||||
} else if (parseInt(value, 10).toString() === value) {
|
||||
} else if (ParsingLib.isInteger(value)) {
|
||||
parsedValue = parseInt(value, 10);
|
||||
}
|
||||
return parsedValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the provided string represents an integer
|
||||
*/
|
||||
static isInteger = (value: string): boolean => {
|
||||
return (parseInt(value, 10).toString() === value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the provided string represents a boolean value
|
||||
*/
|
||||
static isBoolean = (value: string): boolean => {
|
||||
return ['true', 'false'].includes(value);
|
||||
};
|
||||
}
|
||||
|
@ -95,6 +95,7 @@ export interface Product {
|
||||
description?: string,
|
||||
is_active: boolean,
|
||||
product_category_id?: number,
|
||||
is_active_price?: boolean,
|
||||
amount?: number,
|
||||
quantity_min?: number,
|
||||
stock: Stock,
|
||||
|
@ -23,6 +23,7 @@ class Product < ApplicationRecord
|
||||
validates :name, :slug, presence: true
|
||||
validates :slug, uniqueness: true
|
||||
validates :amount, numericality: { greater_than_or_equal_to: 0, allow_nil: true }
|
||||
validates :amount, exclusion: { in: [nil], message: I18n.t('.errors.messages.undefined_in_store') }, if: -> { is_active }
|
||||
|
||||
scope :active, -> { where(is_active: true) }
|
||||
|
||||
|
@ -30,8 +30,6 @@ class ProductService
|
||||
if amount.present?
|
||||
v = amount.to_f
|
||||
|
||||
return nil if v.zero?
|
||||
|
||||
return v * 100
|
||||
end
|
||||
nil
|
||||
|
@ -40,7 +40,7 @@ class ProductImageUploader < CarrierWave::Uploader::Base
|
||||
# Add a white list of extensions which are allowed to be uploaded.
|
||||
# For images you might use something like this:
|
||||
def extension_whitelist
|
||||
%w[jpg jpeg gif png]
|
||||
%w[jpg jpeg gif png webp]
|
||||
end
|
||||
|
||||
def content_type_whitelist
|
||||
|
@ -10,6 +10,10 @@ en:
|
||||
week:
|
||||
one: 'one week'
|
||||
other: '%{count} weeks'
|
||||
activerecord:
|
||||
attributes:
|
||||
product:
|
||||
amount: "The price"
|
||||
errors:
|
||||
#CarrierWave
|
||||
messages:
|
||||
@ -38,6 +42,7 @@ en:
|
||||
invalid_duration: "The allowed duration must be between 1 day and 1 year. Your period is %{DAYS} days long."
|
||||
must_be_in_the_past: "The period must be strictly prior to today's date."
|
||||
registration_disabled: "Registration is disabled"
|
||||
undefined_in_store: "must be defined to make the product available in the store"
|
||||
apipie:
|
||||
api_documentation: "API Documentation"
|
||||
code: "HTTP code"
|
||||
|
@ -212,4 +212,4 @@ en:
|
||||
default: "%a, %d %b %Y %H:%M:%S %z"
|
||||
long: "%B %d, %Y %H:%M"
|
||||
short: "%d %b %H:%M"
|
||||
pm: pm
|
||||
pm: pm
|
||||
|
Loading…
x
Reference in New Issue
Block a user