Skip to content

Commit

Permalink
Merge pull request #2848 from LiteFarmOrg/LF-3556-create-custom-expen…
Browse files Browse the repository at this point in the history
…se-type-read-only-view

Lf 3556 create custom expense type read only view
  • Loading branch information
SayakaOno authored Sep 8, 2023
2 parents 214ff95 + adaa712 commit 44a667f
Show file tree
Hide file tree
Showing 21 changed files with 454 additions and 20 deletions.
12 changes: 12 additions & 0 deletions packages/webapp/public/locales/en/message.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@
"UPDATE": "Successfully updated expenses!"
}
},
"EXPENSE_TYPE": {
"ERROR": {
"ADD": "Failed to create custom expense",
"DELETE": "Failed to delete custom expense",
"UPDATE": "Failed to update custom expense"
},
"SUCCESS": {
"ADD": "Successfully created custom expense",
"DELETE": "Successfully deleted custom expense",
"UPDATE": "Successfully updated custom expense"
}
},
"FARM": {
"ERROR": {
"ADD": "Failed to add farm, please contact litefarm for assistance",
Expand Down
6 changes: 6 additions & 0 deletions packages/webapp/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,10 @@
},
"EXPENSE": {
"ADD_EXPENSE": {
"ADD_CUSTOM_EXPENSE": "Add custom expense",
"CUSTOM_EXPENSE_NAME": "Custom expense name",
"CUSTOM_EXPENSE_TYPE": "Custom expense type",
"DUPLICATE_NAME": "An expense type with this name already exists. Please choose another.",
"FLOW": "expense creation",
"NEW_EXPENSE_ITEM": "New expense item",
"TITLE": "Add expense",
Expand All @@ -506,6 +510,8 @@
"DATE_PLACEHOLDER": "Choose a Date",
"DESELECTING_CATEGORY": "Deselecting a category will remove existing expenses under this category for this expenses log.",
"REMOVE_ALL": "You removed all expenses, click Save to submit.",
"RETIRE_EXPENSE_MESSAGE": "Retiring this expense type will remove it as a possible choice for future expenses. You can still search and filter for historical instances of this expense type on the Finances tab.",
"RETIRE_EXPENSE_TYPE": "Retire expense type",
"TITLE_1": "Edit Expense(1 of 2)",
"TITLE_2": "Edit Expense (2 of 2)"
},
Expand Down
12 changes: 12 additions & 0 deletions packages/webapp/public/locales/es/message.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@
"UPDATE": "Gastos actualizados exitosamente!!"
}
},
"EXPENSE_TYPE": {
"ERROR": {
"ADD": "MISSING",
"DELETE": "MISSING",
"UPDATE": "MISSING"
},
"SUCCESS": {
"ADD": "MISSING",
"DELETE": "MISSING",
"UPDATE": "MISSING"
}
},
"FARM": {
"ERROR": {
"ADD": "No se pudo agregar finca. Por favor contacte LiteFarm por asistencia",
Expand Down
6 changes: 6 additions & 0 deletions packages/webapp/public/locales/es/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,10 @@
},
"EXPENSE": {
"ADD_EXPENSE": {
"ADD_CUSTOM_EXPENSE": "MISSING",
"CUSTOM_EXPENSE_NAME": "MISSING",
"CUSTOM_EXPENSE_TYPE": "MISSING",
"DUPLICATE_NAME": "MISSING",
"FLOW": "MISSING",
"NEW_EXPENSE_ITEM": "MISSING",
"TITLE": "MISSING",
Expand All @@ -505,6 +509,8 @@
"DATE_PLACEHOLDER": "Elige una fecha",
"DESELECTING_CATEGORY": "Anular la selección de una categoría eliminará los gastos existentes bajo dicha categoría para este registro de gastos.",
"REMOVE_ALL": "Ud. eliminó todos los gastos, haga clic en Guardar y así actualizar el cambio.",
"RETIRE_EXPENSE_MESSAGE": "MISSING",
"RETIRE_EXPENSE_TYPE": "MISSING",
"TITLE_1": "Editar gastos (1 de 2)",
"TITLE_2": "Editar gastos (2 de 2)"
},
Expand Down
12 changes: 12 additions & 0 deletions packages/webapp/public/locales/fr/message.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@
"UPDATE": "Dépenses mises à jour avec succès !"
}
},
"EXPENSE_TYPE": {
"ERROR": {
"ADD": "MISSING",
"DELETE": "MISSING",
"UPDATE": "MISSING"
},
"SUCCESS": {
"ADD": "MISSING",
"DELETE": "MISSING",
"UPDATE": "MISSING"
}
},
"FARM": {
"ERROR": {
"ADD": "Impossible d'ajouter une ferme. Veuillez contacter LiteFarm pour obtenir de l'aide",
Expand Down
6 changes: 6 additions & 0 deletions packages/webapp/public/locales/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,10 @@
},
"EXPENSE": {
"ADD_EXPENSE": {
"ADD_CUSTOM_EXPENSE": "MISSING",
"CUSTOM_EXPENSE_NAME": "MISSING",
"CUSTOM_EXPENSE_TYPE": "MISSING",
"DUPLICATE_NAME": "MISSING",
"FLOW": "Création d'une dépense",
"NEW_EXPENSE_ITEM": "MISSING",
"TITLE": "Ajouter une dépense",
Expand All @@ -506,6 +510,8 @@
"DATE_PLACEHOLDER": "Choisissez une date",
"DESELECTING_CATEGORY": "La désélection d'une catégorie supprimera les dépenses existantes dans cette catégorie pour ce journal des dépenses.",
"REMOVE_ALL": "Vous avez supprimé toutes les dépenses, cliquez sur Enregistrer pour valider.",
"RETIRE_EXPENSE_MESSAGE": "MISSING",
"RETIRE_EXPENSE_TYPE": "MISSING",
"TITLE_1": "Modifier les dépenses (1 sur 2)",
"TITLE_2": "Modifier la dépense (2 sur 2)"
},
Expand Down
12 changes: 12 additions & 0 deletions packages/webapp/public/locales/pt/message.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@
"UPDATE": "Despesas atualizadas com sucesso!"
}
},
"EXPENSE_TYPE": {
"ERROR": {
"ADD": "MISSING",
"DELETE": "MISSING",
"UPDATE": "MISSING"
},
"SUCCESS": {
"ADD": "MISSING",
"DELETE": "MISSING",
"UPDATE": "MISSING"
}
},
"FARM": {
"ERROR": {
"ADD": "Falha ao adicionar sito novo, favor entre em contato com o Litefarm para obter assistência",
Expand Down
6 changes: 6 additions & 0 deletions packages/webapp/public/locales/pt/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,10 @@
},
"EXPENSE": {
"ADD_EXPENSE": {
"ADD_CUSTOM_EXPENSE": "MISSING",
"CUSTOM_EXPENSE_NAME": "MISSING",
"CUSTOM_EXPENSE_TYPE": "MISSING",
"DUPLICATE_NAME": "MISSING",
"FLOW": "MISSING",
"NEW_EXPENSE_ITEM": "MISSING",
"TITLE": "MISSING",
Expand All @@ -505,6 +509,8 @@
"DATE_PLACEHOLDER": "Escolha uma data",
"DESELECTING_CATEGORY": "Desmarcar uma categoria removerá as despesas existentes nesta categoria deste registro de despesas.",
"REMOVE_ALL": "Você removeu todas as despesas, clique em Salvar para enviar.",
"RETIRE_EXPENSE_MESSAGE": "MISSING",
"RETIRE_EXPENSE_TYPE": "MISSING",
"TITLE_1": "Editar Despesa (1 de 2)",
"TITLE_2": "Editar Despesa (2 de 2)"
},
Expand Down
31 changes: 31 additions & 0 deletions packages/webapp/src/Routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ const ExpenseCategories = React.lazy(() =>
import('./containers/Finances/NewExpense/ExpenseCategories'),
);
const AddExpense = React.lazy(() => import('./containers/Finances/NewExpense/AddExpense'));
const AddCustomExpense = React.lazy(() =>
import('./containers/Finances/CustomExpenseType/AddSimpleCustomExpense'),
);
const ReadOnlyCustomExpense = React.lazy(() =>
import('./containers/Finances/CustomExpenseType/ReadOnlySimpleCustomExpense'),
);
const EditCustomExpense = React.lazy(() =>
import('./containers/Finances/CustomExpenseType/EditSimpleCustomExpense'),
);
const TempEditExpense = React.lazy(() =>
import('./containers/Finances/EditExpense/TempEditExpense'),
);
Expand Down Expand Up @@ -586,6 +595,17 @@ const Routes = () => {
<Route path="/expense_detail" exact component={ExpenseDetail} />
<Route path="/expense_categories" exact component={ExpenseCategories} />
<Route path="/add_expense" exact component={AddExpense} />
<Route path="/add_custom_expense" exact component={AddCustomExpense} />
<Route
path="/readonly_custom_expense/:expense_type_id"
exact
component={ReadOnlyCustomExpense}
/>
<Route
path="/edit_custom_expense/:expense_type_id"
exact
component={EditCustomExpense}
/>
<Route path="/edit_expense" exact component={TempEditExpense} />
<Route path="/sale_detail" exact component={SaleDetail} />
<Route path="/farm_selection" exact component={ChooseFarm} />
Expand Down Expand Up @@ -867,6 +887,17 @@ const Routes = () => {
<Route path="/expense_detail" exact component={ExpenseDetail} />
<Route path="/expense_categories" exact component={ExpenseCategories} />
<Route path="/add_expense" exact component={AddExpense} />
<Route path="/add_custom_expense" exact component={AddCustomExpense} />
<Route
path="/readonly_custom_expense/:expense_type_id"
exact
component={ReadOnlyCustomExpense}
/>
<Route
path="/edit_custom_expense/:expense_type_id"
exact
component={EditCustomExpense}
/>
<Route path="/crop/new" exact component={AddNewCrop} />
<Route path="/crop/:crop_id/add_crop_variety" exact component={AddCrop} />
<Route
Expand Down
4 changes: 2 additions & 2 deletions packages/webapp/src/apiConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const documentUrl = `${URI}/document`;
export const salesURL = URI + '/sale';
//export const cropSalesURL = URI + '/crop_sale';
export const expenseUrl = URI + '/expense';
export const expenseTypeDefaultUrl = URI + '/expense_type';
export const expenseTypeUrl = `${URI}/expense_type`;
export const revenueTypeUrl = URI + '/revenue_type';
//export const contactURL = URI + '/contact';
//export const farmDataUrl = URI + '/farmdata';
Expand Down Expand Up @@ -96,7 +96,7 @@ export default {
salesURL,
//cropSalesURL,
expenseUrl,
expenseTypeDefaultUrl,
expenseTypeUrl,
revenueTypeUrl,
//contactURL,
//farmDataUrl,
Expand Down
42 changes: 42 additions & 0 deletions packages/webapp/src/components/Form/hookformValidationUtils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
/*
* Copyright 2023 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 <https://www.gnu.org/licenses/>.
*/

// This file contains validation functions for use with react-hook-forms input validator

import i18n from '../../locales/i18n';

export const hookFormMaxValidation = (max = 9999) => ({
Expand All @@ -12,3 +29,28 @@ export const hookFormMaxLengthValidation = (length = 60) => ({
value: length,
message: i18n.t('common:WORD_LIMIT_ERROR', { value: length }),
});
export const hookFormMaxCharsValidation = (max = 9999) => ({
value: max,
message: i18n.t('common:CHAR_LIMIT_ERROR', { value: max }),
});

/**
* Validates if a value is unique within an array of objects based on a specified property.
*
* @param {Array<Object>} objArr - The array of objects to search for duplicates in.
* @param {string} property - The property within each object to check for uniqueness.
* @param {string} message - The error message to return if the value is not unique.
* @returns {(value: any) => string|boolean} A validation function that takes a value to validate and returns
* either the error message (if not unique) or `true` (if unique).
*/
export const hookFormUniquePropertyValidation = (objArr, property, message) => {
return (value) => {
const alreadyExists = objArr.some((item) => {
return item[property] === value;
});
if (alreadyExists) {
return message;
}
return true;
};
};
42 changes: 32 additions & 10 deletions packages/webapp/src/components/Forms/SimpleCustomType/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,30 @@ import PropTypes from 'prop-types';
import { IconLink } from '../../Typography';
import { MdOutlineInventory2 } from 'react-icons/md';
import DeleteBox from '../../Task/TaskReadOnly/DeleteBox';
import { hookFormMaxCharsValidation } from '../../Form/hookformValidationUtils';

// onSubmit: should be used in Add, Edit view
// onClick: should be used in Read-only view
// defaultValue: should be used in Read-only, Edit view
/**
* React component for the addition of custom type with just a name field this form has add,
* edit and read-only functionality.
*
* @param {Object} props - The component's props.
* @param {function} props.handleGoBack - A callback function for handling the "Go Back" action.
* @param {function} [props.onSubmit] - Used in edit and add view, a callback function for handling form submission.
* @param {function} [props.onClick] - Used in read-only view, a callback function for handling button click.
* @param {string} props.view - The view mode ('read-only', 'add' or 'edit').
* @param {string} props.buttonText - The text to display on the main button.
* @param {string} props.pageTitle - The title to display on the page.
* @param {string} props.inputLabel - The label for the input field.
* @param {string} props.customTypeRegister - The name for registering the input field.
* @param {any} [props.defaultValue] - Used in read-only and edit view, the default value for the input field.
* @param {function} [props.onRetire] - A callback function for retiring the custom type.
* @param {string} [props.retireLinkText] - The text for the retirement link.
* @param {string} [props.retireHeader] - The header text for the retirement confirmation box.
* @param {string} [props.retireMessage] - The message text for the retirement confirmation box.
* @param {number} [props.inputMaxChars=100] - The maximum number of characters allowed in the input field.
* @param {function} [props.validateInput] - A custom validation function for the input field.
* @returns {JSX.Element} A React component representing the custom type form.
*/
const PureSimpleCustomType = ({
handleGoBack,
onSubmit,
Expand All @@ -42,21 +62,23 @@ const PureSimpleCustomType = ({
retireLinkText,
retireHeader,
retireMessage,
inputMaxChars = 100,
validateInput,
}) => {
const { t } = useTranslation();
const [isDeleting, setIsDeleting] = useState(false);

const {
handleSubmit,
register,
formState: { errors, isValid },
formState: { errors, isValid, isDirty },
} = useForm({
mode: 'onChange',
defaultValues: { [customTypeRegister]: defaultValue || undefined },
});
const MAX_CHARS = 100;
const readonly = view === 'read-only' || false;
const disabledInput = readonly;
const disabledButton = (!isValid || !isDirty) && !readonly;

return (
<Form
Expand All @@ -65,7 +87,7 @@ const PureSimpleCustomType = ({
<Button
color={'primary'}
fullLength
disabled={!isValid}
disabled={disabledButton}
onClick={onClick ? onClick : undefined}
>
{buttonText}
Expand All @@ -78,10 +100,8 @@ const PureSimpleCustomType = ({
label={inputLabel}
hookFormRegister={register(customTypeRegister, {
required: true,
maxLength: {
value: MAX_CHARS,
message: t('common:CHAR_LIMIT_ERROR', { value: MAX_CHARS }),
},
maxLength: hookFormMaxCharsValidation(inputMaxChars),
validate: validateInput,
})}
name={customTypeRegister}
errors={getInputErrors(errors, customTypeRegister)}
Expand Down Expand Up @@ -139,6 +159,8 @@ PureSimpleCustomType.propTypes = {
retireLinkText: PropTypes.string,
retireHeader: PropTypes.string,
retireMessage: PropTypes.string,
inputMaxChars: PropTypes.number,
validateInput: PropTypes.func,
};

export default PureSimpleCustomType;
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,15 @@ export default function DeleteBox({
</h3>
<p>{message}</p>
<div className={styles.deleteBoxButtons}>
<Button data-cy="taskReadOnly-complete" color={'secondary'} onClick={onCancel}>
<Button
type="button"
data-cy="taskReadOnly-complete"
color={'secondary'}
onClick={onCancel}
>
{t('common:CANCEL')}
</Button>
<Button data-cy="taskReadOnly-complete" color={color} onClick={onOk}>
<Button type="button" data-cy="taskReadOnly-complete" color={color} onClick={onOk}>
{primaryButtonLabel}
</Button>
</div>
Expand Down
Loading

0 comments on commit 44a667f

Please sign in to comment.