From e156a9eba71dfad564064ba373f59b9e72e21ea8 Mon Sep 17 00:00:00 2001 From: Guanjie Date: Sun, 1 Dec 2024 09:54:39 -0500 Subject: [PATCH 01/53] refactored Access Token List --- admin/src/Settings/AccessTokenList.jsx | 72 ++++++++++++-------------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/admin/src/Settings/AccessTokenList.jsx b/admin/src/Settings/AccessTokenList.jsx index 86a5a2b2a..a520d2b35 100644 --- a/admin/src/Settings/AccessTokenList.jsx +++ b/admin/src/Settings/AccessTokenList.jsx @@ -1,44 +1,38 @@ -import React from "react"; -import Collection from "../Models/Collection"; -import AccessToken from "../Models/AccessToken"; -import CollectionTable from "../Components/CollectionTable"; +import React, { useRef } from "react"; import auth from "../auth"; +import CollectionTable from "../Components/CollectionTable"; import DateTimeShow from "../Components/DateTimeShow"; +import AccessToken from "../Models/AccessToken"; +import Collection from "../Models/Collection"; -export default class AccessTokenList extends React.Component { - constructor(props) { - super(props); - this.collection = new Collection({ type: AccessToken }); - this.state = {}; - } +export default function AccessTokenList() { + const collection = useRef(new Collection({ type: AccessToken })); - render() { - return ( - ( - - - {auth.getAccessToken() === item.access_token ? ( - - ) : null} - - {item.access_token} - {item.browser} - {item.ip} - - - - - )} - /> - ); - } + return ( + ( + + + {auth.getAccessToken() === item.access_token ? ( + + ) : null} + + {item.access_token} + {item.browser} + {item.ip} + + + + + )} + /> + ); } From a43d9226766dd16a6acdd17b925836246c1ff4bc Mon Sep 17 00:00:00 2001 From: Shanting Hou Date: Sun, 1 Dec 2024 13:32:22 -0500 Subject: [PATCH 02/53] convert logout component to function --- admin/src/Components/Logout.jsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/admin/src/Components/Logout.jsx b/admin/src/Components/Logout.jsx index 8bf0bc95f..86143e82f 100644 --- a/admin/src/Components/Logout.jsx +++ b/admin/src/Components/Logout.jsx @@ -1,14 +1,14 @@ -import React from "react"; -import { browserHistory } from "../browser_history"; +import React, { useEffect } from "react"; import auth from "../auth"; +import { browserHistory } from "../browser_history"; -export default class Logout extends React.Component { - componentDidMount() { +const Logout = () => { + useEffect(() => { auth.logout(); browserHistory.push("/"); - } + }, []); + + return

You are now logged out

; +}; - render() { - return

You are now logged out

; - } -} +export default Logout; \ No newline at end of file From 1269bd84c994bcf07b24423cb3fd39178fc7506a Mon Sep 17 00:00:00 2001 From: Shanting Hou Date: Sun, 1 Dec 2024 13:35:56 -0500 Subject: [PATCH 03/53] formatting --- admin/src/Components/Logout.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/src/Components/Logout.jsx b/admin/src/Components/Logout.jsx index 86143e82f..458fd7db6 100644 --- a/admin/src/Components/Logout.jsx +++ b/admin/src/Components/Logout.jsx @@ -11,4 +11,4 @@ const Logout = () => { return

You are now logged out

; }; -export default Logout; \ No newline at end of file +export default Logout; From 575f1f08453ea54cce1e157080d18b560c9e6e0d Mon Sep 17 00:00:00 2001 From: ilong4rennes Date: Mon, 2 Dec 2024 22:25:26 +0800 Subject: [PATCH 04/53] refactored login class to function --- admin/src/Components/Login.jsx | 131 ++++++++++++++++----------------- 1 file changed, 65 insertions(+), 66 deletions(-) diff --git a/admin/src/Components/Login.jsx b/admin/src/Components/Login.jsx index 6a99da330..8f2d01f69 100644 --- a/admin/src/Components/Login.jsx +++ b/admin/src/Components/Login.jsx @@ -1,13 +1,16 @@ -import React from "react"; +import React, { useRef } from "react"; import { Link } from "react-router-dom"; import auth from "../auth"; import { showError } from "../message"; -export default class Login extends React.Component { - login(e) { +const Login = () => { + const usernameRef = useRef(null); + const passwordRef = useRef(null); + + const login = (e) => { e.preventDefault(); - const username = this.username.value; - const password = this.password.value; + const username = usernameRef.current.value; + const password = passwordRef.current.value; if (!username || !password) { showError("Du måste fylla i email/medlemsnummer och lösenord"); @@ -15,73 +18,69 @@ export default class Login extends React.Component { } auth.login(username, password); - } + }; - render() { - return ( -
-
+
+
- -
-

Logga in

-
+
+

Logga in

+
-
-
- - { - this.username = c; - }} - className="uk-form-large uk-form-width-large" - type="text" - placeholder="Email/Medlemsnummer" - autoComplete="username" - /> -
+
+
+ +
+
-
-
- - { - this.password = c; - }} - className="uk-form-large uk-form-width-large" - type="password" - placeholder="Lösenord" - autoComplete="current-password" - /> -
+
+
+ +
+
-
- -
+
+ +
-
- - Glömt ditt lösenord? - -
- -
+
+ + Glömt ditt lösenord? + +
+
- ); - } -} +
+ ); +}; + +export default Login; From ea865dc7fa078dab967f74fbcb1651ab4eb2aefa Mon Sep 17 00:00:00 2001 From: ilong4rennes Date: Tue, 3 Dec 2024 00:47:21 +0800 Subject: [PATCH 05/53] changed class to function in DateTimeInput.jsx --- admin/src/Components/DateTimeInput.jsx | 101 +++++++++++-------------- 1 file changed, 46 insertions(+), 55 deletions(-) diff --git a/admin/src/Components/DateTimeInput.jsx b/admin/src/Components/DateTimeInput.jsx index 939aa51b4..3678313b8 100644 --- a/admin/src/Components/DateTimeInput.jsx +++ b/admin/src/Components/DateTimeInput.jsx @@ -1,64 +1,55 @@ -import React from "react"; import classNames from "classnames/bind"; +import React, { useEffect, useState } from "react"; import { dateTimeToStr } from "../utils"; -export default class DateTimeInput extends React.Component { - constructor(props) { - super(props); - this.state = { - value: "", - }; - } - - componentDidMount() { - const { model, name } = this.props; - this.unsubscribe = model.subscribe(() => - this.setState({ value: dateTimeToStr(model[name]) }), - ); - } +const DateTimeInput = ({ model, name, title, icon, placeholder }) => { + const [value, setValue] = useState(""); - componentWillUnmount() { - this.unsubscribe(); - } + useEffect(() => { + const unsubscribe = model.subscribe(() => { + setValue(dateTimeToStr(model[name])); + }); - render() { - const { name, title, icon, placeholder } = this.props; - const { value } = this.state; + return () => { + unsubscribe(); + }; + }, [model, name]); - const classes = classNames(name, { - "uk-form-row": true, - selected: this.state.selected, - changed: this.state.isDirty, - }); + const classes = classNames(name, { + "uk-form-row": true, + selected: false, + changed: false, + }); - const input = ( - - ); + const input = ( + + ); - return ( -
- -
- {icon ? ( -
- - {input} -
- ) : ( - input - )} -
+ return ( +
+ +
+ {icon ? ( +
+ + {input} +
+ ) : ( + input + )}
- ); - } -} +
+ ); +}; + +export default DateTimeInput; From 219367f6834d3fda6f212b17529cf6f3683c0207 Mon Sep 17 00:00:00 2001 From: Shanting Hou Date: Mon, 2 Dec 2024 12:11:12 -0500 Subject: [PATCH 06/53] message form refactor --- admin/src/Components/MessageForm.jsx | 163 +++++++++++++-------------- 1 file changed, 79 insertions(+), 84 deletions(-) diff --git a/admin/src/Components/MessageForm.jsx b/admin/src/Components/MessageForm.jsx index 2ce11a06d..07dc52537 100644 --- a/admin/src/Components/MessageForm.jsx +++ b/admin/src/Components/MessageForm.jsx @@ -1,11 +1,10 @@ -import React from "react"; -import { withRouter } from "react-router"; -import TextInput from "./TextInput"; -import Textarea from "./Textarea"; +import React, { useCallback, useEffect, useState } from "react"; import { Async } from "react-select"; import { get } from "../gateway"; import Group from "../Models/Group"; import Member from "../Models/Member"; +import Textarea from "./Textarea"; +import TextInput from "./TextInput"; const groupOption = (d) => { const id = d[Group.model.id]; @@ -30,30 +29,24 @@ const memberOption = (d) => { }; }; -class MessageForm extends React.Component { - constructor(props) { - super(props); - this.state = { - messageType: null, - saveDisabled: true, - }; - } +const MessageForm = ({ message, onSave, recipientSelect }) => { + const [sendDisabled, setSendDisabled] = useState(true); + const [recipients, setRecipients] = useState([]); + const [bodyLength, setBodyLength] = useState(message.body.length); - componentDidMount() { - const { message } = this.props; - this.unsubscribe = message.subscribe(() => { - this.setState({ - sendDisabled: !message.canSave(), - recipients: message.recipients, - }); + useEffect(() => { + const unsubscribe = message.subscribe(() => { + setSendDisabled(!message.canSave()); + setRecipients(message.recipients); + setBodyLength(message.body.length); // Update body length whenever the message updates }); - } - componentWillUnmount() { - this.unsubscribe(); - } + return () => { + unsubscribe(); + }; + }, [message]); - loadOptions(inputValue, callback) { + const loadOptions = useCallback((inputValue, callback) => { Promise.all([ get({ url: "/membership/group", @@ -78,70 +71,72 @@ class MessageForm extends React.Component { .concat(members.map((d) => memberOption(d))), ), ); - } - - render() { - const { message, onSave: onSend, recipientSelect } = this.props; - const { sendDisabled, recipients } = this.state; - - return ( -
{ - e.preventDefault(); - onSend(); - return false; - }} - > - {recipientSelect ? ( -
- -
- e.value} - getOptionLabel={(e) => e.label} - loadOptions={(v, c) => this.loadOptions(v, c)} - value={recipients} - onChange={(values) => - (message.recipients = values) - } - /> -
-
- ) : ( - "" - )} + }, []); - - - )} - {!this.state.csv_content && ( - this.exportMembers()} - > - Exportera alla aktiva medlemmar som CSV - {this.state.state === "loading" ? "..." : ""} - - )} -
- ); - } + return ( +
+

Exportera medlemslista

+ {csvContent && ( + + )} + {!csvContent && ( + + Exportera alla aktiva medlemmar som CSV + {state === "loading" ? "..." : ""} + + )} +
+ ); } export default MemberExport; diff --git a/admin/src/Membership/MembershipPeriodsInput.js b/admin/src/Membership/MembershipPeriodsInput.js index 45c27b90f..e965494fd 100644 --- a/admin/src/Membership/MembershipPeriodsInput.js +++ b/admin/src/Membership/MembershipPeriodsInput.js @@ -1,132 +1,118 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import CategoryPeriodsInput from "../Components/CategoryPeriodsInput"; import CategoryPeriods from "../Models/CategoryPeriods"; import { calculateSpanDiff, filterPeriods } from "../Models/Span"; import auth from "../auth"; import { post } from "../gateway"; -export default class MembershipPeriodsInput extends React.Component { - constructor(props) { - super(props); - this.unsubscribe = []; - this.categoryPeriodsList = [ - new CategoryPeriods({ category: "labaccess" }), - new CategoryPeriods({ category: "membership" }), - new CategoryPeriods({ category: "special_labaccess" }), - ]; - this.state = { showHistoric: true, saveDisabled: true }; - } +export default function MembershipPeriodsInput(props) { + const [showHistoric, setShowHistoric] = useState(true); + const [saveDisabled, setSaveDisabled] = useState(true); - canSave() { + const categoryPeriodsList = [ + new CategoryPeriods({ category: "labaccess" }), + new CategoryPeriods({ category: "membership" }), + new CategoryPeriods({ category: "special_labaccess" }), + ]; + + const canSave = () => { return ( - this.categoryPeriodsList.every((c) => c.isValid()) && - this.categoryPeriodsList.some((c) => c.isDirty()) + categoryPeriodsList.every((c) => c.isValid()) && + categoryPeriodsList.some((c) => c.isDirty()) ); - } + }; - componentDidMount() { - this.unsubscribe.push( - this.props.spans.subscribe(({ items }) => { - this.categoryPeriodsList.forEach((periods) => + useEffect(() => { + const unsubscribe = []; + unsubscribe.push( + props.spans.subscribe(({ items }) => { + categoryPeriodsList.forEach((periods) => periods.replace(filterPeriods(items, periods.category)), ); }), ); - this.categoryPeriodsList.forEach((cp) => { - this.unsubscribe.push( - cp.subscribe(() => - this.setState({ saveDisabled: !this.canSave() }), - ), - ); + categoryPeriodsList.forEach((cp) => { + unsubscribe.push(cp.subscribe(() => setSaveDisabled(!canSave()))); }); - } - - componentWillUnmount() { - this.unsubscribe.forEach((u) => u()); - } - render() { - const { showHistoric, saveDisabled } = this.state; - const { member_id, spans } = this.props; + return () => { + unsubscribe.forEach((u) => u()); + }; + }, [props.spans, categoryPeriodsList]); - const onSave = () => { - // Important, need to collect spans to delete and add before doing anything, when spans changes - // subscriptions on spans will start causing changes of category periods. - const deleteSpans = []; - const addSpans = []; - this.categoryPeriodsList.forEach((cp) => { - cp.merge(); - calculateSpanDiff({ - items: this.props.spans.items, - categoryPeriods: cp, - member_id, - deleteSpans, - addSpans, - }); + const onSave = () => { + const deleteSpans = []; + const addSpans = []; + categoryPeriodsList.forEach((cp) => { + cp.merge(); + calculateSpanDiff({ + items: props.spans.items, + categoryPeriods: cp, + member_id: props.member_id, + deleteSpans, + addSpans, }); + }); - const deleteIds = deleteSpans.map((s) => s.id).join(","); - const timestamp = new Date().getTime().toString(); - addSpans.forEach( - (s, i) => - (s.creation_reason = ( - timestamp + - i + - " gui_edit:" + - auth.getUsername() + - " replacing:" + - deleteIds - ).slice(0, 255)), - ); + const deleteIds = deleteSpans.map((s) => s.id).join(","); + const timestamp = new Date().getTime().toString(); + addSpans.forEach( + (s, i) => + (s.creation_reason = ( + timestamp + + i + + " gui_edit:" + + auth.getUsername() + + " replacing:" + + deleteIds + ).slice(0, 255)), + ); - const promises = []; - promises.push(...deleteSpans.map((s) => s.del())); - promises.push(...addSpans.map((s) => s.save())); - Promise.all(promises).then(() => { - spans.fetch(); + const promises = []; + promises.push(...deleteSpans.map((s) => s.del())); + promises.push(...addSpans.map((s) => s.save())); + Promise.all(promises).then(() => { + props.spans.fetch(); - post({ - url: `/webshop/member/${member_id}/ship_labaccess_orders`, - expectedDataStatus: "ok", - }); + post({ + url: `/webshop/member/${props.member_id}/ship_labaccess_orders`, + expectedDataStatus: "ok", }); - }; + }); + }; - return ( - { - e.preventDefault(); - onSave(); - return false; - }} - > - - - this.setState({ showHistoric: e.target.checked }) - } + return ( + { + e.preventDefault(); + onSave(); + return false; + }} + > + + setShowHistoric(e.target.checked)} + /> + {categoryPeriodsList.map((cp) => ( + - {this.categoryPeriodsList.map((cp) => ( - - ))} - - - ); - } + ))} + + + ); } diff --git a/admin/src/Membership/SpanShow.jsx b/admin/src/Membership/SpanShow.jsx index 2f8de734b..2b331a7e3 100644 --- a/admin/src/Membership/SpanShow.jsx +++ b/admin/src/Membership/SpanShow.jsx @@ -1,42 +1,31 @@ -import React from "react"; -import Span from "../Models/Span"; +import React, { useEffect, useState } from "react"; import * as _ from "underscore"; +import Span from "../Models/Span"; -class SpanShow extends React.Component { - constructor(props) { - super(props); - const { span_id } = props.match.params; - this.span = Span.get(span_id); - this.state = { data: {} }; - } +export default function SpanShow(props) { + const { span_id } = props.match.params; + const [data, setData] = useState({}); - componentDidMount() { - this.unsubscribe = this.span.subscribe(() => - this.setState({ data: this.span.saved }), - ); - } + const span = Span.get(span_id); - componentWillUnmount() { - this.unsubscribe(); - } + useEffect(() => { + const unsubscribe = span.subscribe(() => setData(span.saved)); - render() { - const { data } = this.state; + // Cleanup subscription on unmount + return () => unsubscribe(); + }, [span]); - return ( -
-

Medlemsperiod {data.span_id}

-
- {_.keys(data).map((key) => ( -
-
{key}:
-
{data[key]}
-
- ))} -
-
- ); - } + return ( +
+

Medlemsperiod {data.span_id}

+
+ {_.keys(data).map((key) => ( +
+
{key}:
+
{data[key]}
+
+ ))} +
+
+ ); } - -export default SpanShow; From 6ed27d11a523d62440562b1474988c0bab4a0c6f Mon Sep 17 00:00:00 2001 From: ilong4rennes Date: Fri, 6 Dec 2024 11:26:18 -0500 Subject: [PATCH 45/53] update accounting product --- admin/src/Sales/AccountingProduct.js | 674 +++++++++++++++++++-------- 1 file changed, 474 insertions(+), 200 deletions(-) diff --git a/admin/src/Sales/AccountingProduct.js b/admin/src/Sales/AccountingProduct.js index 28fe95b27..f926488eb 100644 --- a/admin/src/Sales/AccountingProduct.js +++ b/admin/src/Sales/AccountingProduct.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React from "react"; import { Link } from "react-router-dom"; import Select from "react-select"; import * as _ from "underscore"; @@ -6,73 +6,125 @@ import CollectionTable from "../Components/CollectionTable"; import Currency from "../Components/Currency"; import SearchBox from "../Components/SearchBox"; import Collection from "../Models/Collection"; +import CollectionNavigation from "../Models/CollectionNavigation"; import Product from "../Models/Product"; import ProductAccountsCostCenters from "../Models/ProductAccountsCostCenters"; import { get } from "../gateway"; import { showError } from "../message"; -const AccountingProduct = () => { - const [categories, setCategories] = useState(null); - const [transactionAccount, setTransactionAccount] = useState(null); - const [transactionCostCenter, setTransactionCostCenter] = useState(null); - const [selectedOptionAccount, setSelectedOptionAccount] = useState(null); - const [showOptionsAccount, setShowOptionsAccount] = useState([]); - const [selectedOptionCostCenter, setSelectedOptionCostCenter] = - useState(null); - const [showOptionsCostCenter, setShowOptionsCostCenter] = useState([]); - const [selectedProductId, setSelectedProductId] = useState([]); - - const collection = new Collection({ - type: Product, - url: "/webshop/product", - expand: "product_accounting", - page: 1, - pageSize: 0, - filter_out_key: "type", - filter_out_value: "debit", - includeDeleted: true, - }); - - useEffect(() => { - // Fetch categories +const filterOptions_account = (items_account, options_account) => { + const current = new Set(items_account.map((i) => i.id)); + return options_account.filter((o) => !current.has(o.id)); +}; + +const filterOptions_cost_center = (items_cost_center, options_cost_center) => { + const current = new Set(items_cost_center.map((i) => i.id)); + return options_cost_center.filter((o) => !current.has(o.id)); +}; + +const updateOptions_account = (options_account) => (prevState) => { + let options = [ + { + account: "No modification", + id: 0, + description: "Do not change account", + }, + ].concat(filterOptions_account(prevState.items_account, options_account)); + return { + showOptions_account: options, + options_account, + }; +}; + +const updateOptions_cost_center = (options_cost_center) => (prevState) => { + let options = [ + { + cost_center: "No modification", + id: 0, + description: "Do not change cost center", + }, + ].concat( + filterOptions_cost_center( + prevState.items_cost_center, + options_cost_center, + ), + ); + return { + showOptions_cost_center: options, + options_cost_center, + }; +}; + +class AccountingProduct extends CollectionNavigation { + constructor(props) { + super(props); + const { search, page } = this.state; + this.collection = new Collection({ + type: Product, + url: "/webshop/product", + expand: "product_accounting", + search: search, + page: page, + pageSize: 0, + filter_out_key: "type", + filter_out_value: "debit", + includeDeleted: true, + }); + this.state = { + categories: null, + items_account: [], + options_account: [], + selectedOption_account: null, + items_cost_center: [], + options_cost_center: [], + selectedOption_cost_center: null, + accounts: null, + cost_centers: null, + selected_product_id: [], + latest_selected_row_index: null, + }; + + this.product = new Product(); + this.product_accounts_cost_centers = new ProductAccountsCostCenters(); + get({ url: "/webshop/category", params: { page_size: 0 } }) - .then((data) => { - const fetchedCategories = _.reduce( - data.data, - (obj, item) => { - obj[item.id] = item.name; - return obj; - }, - {}, - ); - setCategories(fetchedCategories); - }) + .then( + (data) => { + const categories = _.reduce( + data.data, + (obj, item) => { + obj[item.id] = item.name; + return obj; + }, + {}, + ); + this.setState({ categories }); + }, + () => null, + ) .catch((error) => { showError("

Failed to find categories

" + error.message); }); - // Fetch transaction accounts - get({ url: "/webshop/transaction_account", params: { page_size: 0 } }) - .then((data) => { - const fetchedAccounts = _.reduce( - data.data, - (obj, item) => { - obj[item.id] = item.account; - return obj; - }, - {}, - ); - setTransactionAccount(fetchedAccounts); - setShowOptionsAccount( - [ - { - account: "No modification", - id: 0, - description: "Do not change account", + get({ + url: "/webshop/transaction_account", + params: { page_size: 0 }, + }) + .then( + (data) => { + const transaction_account = _.reduce( + data.data, + (obj, item) => { + obj[item.id] = item.account; + return obj; }, - ].concat(data.data), - ); - }) + {}, + ); + this.setState({ transaction_account }); + this.setState(updateOptions_account(data.data)); + }, + () => null, + ) .catch((error) => { showError( "

Failed to find transaction accounts

" + @@ -80,179 +132,401 @@ const AccountingProduct = () => { ); }); - // Fetch transaction cost centers get({ url: "/webshop/transaction_cost_center", params: { page_size: 0 }, }) - .then((data) => { - const fetchedCostCenters = _.reduce( - data.data, - (obj, item) => { - obj[item.id] = item.cost_center; - return obj; - }, - {}, - ); - setTransactionCostCenter(fetchedCostCenters); - setShowOptionsCostCenter( - [ - { - cost_center: "No modification", - id: 0, - description: "Do not change cost center", + .then( + (data) => { + const transaction_cost_center = _.reduce( + data.data, + (obj, item) => { + obj[item.id] = item.cost_center; + return obj; }, - ].concat(data.data), - ); - }) + {}, + ); + this.setState({ transaction_cost_center }); + this.setState(updateOptions_cost_center(data.data)); + }, + () => null, + ) .catch((error) => { showError( "

Failed to find transaction cost centers

" + error.message, ); }); - }, []); + } - const onSave = (account, costCenter) => { - selectedProductId.forEach((productId) => { - get({ url: "/webshop/accounting", params: { page_size: 0 } }).then( - (data) => { + selectOptionAccount(account) { + this.setState({ selectedOption_account: account }); + } + + selectOptionCostCenter(cost_center) { + this.setState({ selectedOption_cost_center: cost_center }); + } + + deserialize(x) { + return x; + } + + changeColor(activate = true, table_index) { + const row = document.getElementsByTagName("tr"); + table_index.forEach(colorRows); + + function colorRows(id) { + if (activate) { + row[id + 1].style.backgroundColor = "#ddd"; + } else { + row[id + 1].style.backgroundColor = ""; + } + } + } + + onSave(account, cost_center) { + const productIds = this.state.selected_product_id; + + productIds.forEach((product_id) => { + get({ url: "/webshop/accounting", params: { page_size: 0 } }) + .then((data) => { const accountings = data.data; - const currentAccounting = accountings.find( - (p) => p.product_id === productId, + const current_accounting = accountings.filter( + (p) => p.product_id === product_id, ); - - const accountingElementCredit = - new ProductAccountsCostCenters(currentAccounting); - accountingElementCredit.product_id = productId; - accountingElementCredit.type = "credit"; - accountingElementCredit.fraction = 100; + const accounting_element_debit = + new ProductAccountsCostCenters( + current_accounting.filter( + (p) => p.type === "debit", + )[0], + ); + accounting_element_debit.product_id = product_id; + accounting_element_debit.type = "debit"; + accounting_element_debit.account_id = 1; + accounting_element_debit.fraction = 100; + accounting_element_debit.save().then(() => { + accounting_element_debit.reset(); + }); + const accounting_element_credit = + new ProductAccountsCostCenters( + current_accounting.filter( + (p) => p.type === "credit", + )[0], + ); + accounting_element_credit.product_id = product_id; + accounting_element_credit.type = "credit"; + accounting_element_credit.fraction = 100; if (account && account.id !== 0) { - accountingElementCredit.account_id = account.id; + accounting_element_credit.account_id = account.id; } - if (costCenter && costCenter.id !== 0) { - accountingElementCredit.cost_center_id = costCenter.id; + if (cost_center && cost_center.id !== 0) { + accounting_element_credit.cost_center_id = + cost_center.id; } - - return accountingElementCredit.save().then(() => { - collection.fetch(); + return accounting_element_credit; + }) + .then((accounting_element_credit) => { + accounting_element_credit.save().then(() => { + accounting_element_credit.reset(); + this.collection.fetch(); }); - }, - ); + return accounting_element_credit; + }); }); - setSelectedProductId([]); - }; + if (this.state.selected_product_id.length > 0) { + const table_index = []; + this.state.selected_product_id.forEach((product_id) => { + table_index.push( + this.collection.items + .map(function (e) { + return e.saved.id; + }) + .indexOf(product_id), + ); + }); + this.changeColor(false, table_index); + } + + this.setState({ selected_product_id: [] }); + this.setState({ latest_selected_row_index: null }); + } - const setSelectedRow = (event, element) => { - // const tableIndex = collection.items.findIndex((item) => item.id === element.id); - if (selectedProductId.includes(element.id)) { - setSelectedProductId((prev) => - prev.filter((id) => id !== element.id), - ); + updateAddSelectedProductId(current_product_ids, element) { + if (Array.isArray(element)) { + this.setState({ + selected_product_id: current_product_ids.concat(element), + }); } else { - setSelectedProductId((prev) => [...prev, element.id]); + this.setState({ + selected_product_id: current_product_ids.concat([element]), + }); } - }; + } - return ( -
-
-

Hantera produkter för bokföring

-

- På denna sida kan du ange och ta bort konton och - kostnadsställen för produkter. Markera en eller flera - produkter i listan och välj vilket konto och kostnadsställe - du vill applicera. -

-
+ updateRemoveSelectedProductId(current_product_ids, element) { + this.setState({ + selected_product_id: current_product_ids.filter( + (item) => item !== element, + ), + }); + } -
{ - e.preventDefault(); - onSave(selectedOptionAccount, selectedOptionCostCenter); - }} - > -
- - g.id} - getOptionLabel={(g) => - `${g.cost_center} : ${g.description}` + setSelectedRow(evt, element) { + const table_index = this.collection.items + .map(function (e) { + return e.saved.id; + }) + .indexOf(element.id); + const selected_product_id = this.state.selected_product_id; + const latest_selected_row_index = this.state.latest_selected_row_index; + + if (selected_product_id.indexOf(element.id) > -1) { + this.changeColor(false, [table_index]); + this.updateRemoveSelectedProductId(selected_product_id, element.id); + } else { + if (evt.shiftKey) { + if (latest_selected_row_index === null) { + this.changeColor(true, [table_index]); + this.updateAddSelectedProductId( + selected_product_id, + element.id, + ); + } else { + const new_product_id = []; + const new_table_index = []; + if (table_index > latest_selected_row_index) { + for ( + let i = latest_selected_row_index + 1; + i <= table_index; + i++ + ) { + if ( + selected_product_id.indexOf( + this.collection.items[i].id, + ) === -1 + ) { + new_product_id.push( + this.collection.items[i].id, + ); + new_table_index.push(i); + } } - onChange={(costCenter) => - setSelectedOptionCostCenter(costCenter) + } else { + for ( + let i = table_index; + i <= latest_selected_row_index - 1; + i++ + ) { + if ( + selected_product_id.indexOf( + this.collection.items[i].id, + ) === -1 + ) { + new_product_id.push( + this.collection.items[i].id, + ); + new_table_index.push(i); + } } + } + this.changeColor(true, new_table_index); + this.updateAddSelectedProductId( + selected_product_id, + new_product_id, + ); + } + this.updateLatestSelectedRowIndex(table_index); + } else if (evt.ctrlKey) { + this.changeColor(true, [table_index]); + this.updateAddSelectedProductId( + selected_product_id, + element.id, + ); + this.updateLatestSelectedRowIndex(table_index); + } else { + this.updateResetSelectedProductId(element.id, table_index); + this.updateLatestSelectedRowIndex(table_index); + } + } + } + + render() { + const { categories, transaction_account, transaction_cost_center } = + this.state; + const { + selectedOption_account, + showOptions_account, + selectedOption_cost_center, + showOptions_cost_center, + } = this.state; + + return ( +
+
+

Hantera produkter för bokföring

+

+ På denna sida kan du ange och ta bort konton och + kostnadsställen för produkter. Markera en eller flera + produkter i listan och välj vilket konto och + kostnadsställe du vill applicera. +

+
+ + { + e.preventDefault(); + this.onSave( + selectedOption_account, + selectedOption_cost_center, + ); + return false; + }} + > +
+ + g.id} + getOptionLabel={(g) => + g.cost_center + " : " + g.description + } + onChange={(cost_center) => + this.selectOptionCostCenter(cost_center) + } + /> +
+ +
+ +
+ + +
+ + + ( + + this.setSelectedRow(event, item) + } + > + + + {item.name} + + + + {categories + ? categories[item.category_id] + : item.category_id} + + + + kr + + + {item.smallest_multiple === 1 + ? item.unit + : item.smallest_multiple + + " " + + item.unit} + + + {transaction_account + ? transaction_account[item.account_id] + : item.account_id} + + + {transaction_cost_center + ? transaction_cost_center[ + item.cost_center_id + ] + : item.cost_center} + + + )} + onPageNav={this.onPageNav} /> -
- - - - -
- {}} /> - - ( - setSelectedRow(event, item)}> - - - {item.name} - - - - {categories - ? categories[item.category_id] - : item.category_id} - - - kr - - {item.unit} - {transactionAccount?.[item.account_id]} - - {transactionCostCenter?.[item.cost_center_id]} - - - )} - /> +
-
- ); -}; + ); + } +} export default AccountingProduct; From 112ec5c52b77098842bbbe1279ab1112057b602a Mon Sep 17 00:00:00 2001 From: ilong4rennes Date: Sat, 7 Dec 2024 19:49:13 -0500 Subject: [PATCH 46/53] fix span error --- admin/src/Membership/MemberBoxSpans.jsx | 78 ++++++++++--------- .../src/Membership/MembershipPeriodsInput.js | 52 ++++++++----- 2 files changed, 74 insertions(+), 56 deletions(-) diff --git a/admin/src/Membership/MemberBoxSpans.jsx b/admin/src/Membership/MemberBoxSpans.jsx index 1b140ae75..e3d7ef482 100644 --- a/admin/src/Membership/MemberBoxSpans.jsx +++ b/admin/src/Membership/MemberBoxSpans.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import "react-day-picker/lib/style.css"; import { Link } from "react-router-dom"; import CollectionTable from "../Components/CollectionTable"; @@ -11,51 +11,53 @@ import { get } from "../gateway"; import { confirmModal } from "../message"; import MembershipPeriodsInput from "./MembershipPeriodsInput"; -const MemberBoxSpans = (props) => { - const collection = new Collection({ - type: Span, - url: `/membership/member/${props.match.params.member_id}/spans`, - pageSize: 0, - includeDeleted: true, - }); +function MemberBoxSpans(props) { + const memberId = props.match.params.member_id; - const [pendingLabAccessDays, setPendingLabAccessDays] = useState("?"); + const [, setItems] = useState([]); + const [pendingLabaccessDays, setPendingLabaccessDays] = useState("?"); - useEffect(() => { - // Fetch pending actions - const fetchPendingActions = async () => { - const response = await get({ - url: `/membership/member/${props.match.params.member_id}/pending_actions`, - }); - const sumPendingLabAccessDays = response.data.reduce( - (acc, value) => { - if (value.action.action === ADD_LABACCESS_DAYS) - return acc + value.action.value; - return acc; - }, - 0, - ); - setPendingLabAccessDays(sumPendingLabAccessDays); - }; + const collectionRef = useRef( + new Collection({ + type: Span, + url: `/membership/member/${memberId}/spans`, + pageSize: 0, + includeDeleted: true, + }), + ); - fetchPendingActions(); + // Fetch pending actions on mount + useEffect(() => { + get({ url: `/membership/member/${memberId}/pending_actions` }).then( + (r) => { + const sum_pending_labaccess_days = r.data.reduce( + (acc, value) => { + if (value.action.action === ADD_LABACCESS_DAYS) + return acc + value.action.value; + return acc; + }, + 0, + ); + setPendingLabaccessDays(sum_pending_labaccess_days); + }, + ); + }, [memberId]); - // Subscribe to collection - const unsubscribe = collection.subscribe(() => { - // No need to handle the subscription data since it's unused + // Subscribe to collection changes + useEffect(() => { + const unsubscribe = collectionRef.current.subscribe(({ items }) => { + setItems(items); }); - - // Cleanup on component unmount return () => { unsubscribe(); }; - }, [props.match.params.member_id]); + }, []); const deleteItem = (item) => confirmModal(item.deleteConfirmMessage()) .then(() => item.del()) .then( - () => collection.fetch(), + () => collectionRef.current.fetch(), () => null, ); @@ -63,18 +65,18 @@ const MemberBoxSpans = (props) => {

Medlemsperioder

- {pendingLabAccessDays} dagar labaccess kommer läggas till + {pendingLabaccessDays} dagar labaccess kommer läggas till vid en nyckelsynkronisering.


Spans


{ />
); -}; +} export default MemberBoxSpans; diff --git a/admin/src/Membership/MembershipPeriodsInput.js b/admin/src/Membership/MembershipPeriodsInput.js index e965494fd..bdc1c76de 100644 --- a/admin/src/Membership/MembershipPeriodsInput.js +++ b/admin/src/Membership/MembershipPeriodsInput.js @@ -1,54 +1,60 @@ -import React, { useEffect, useState } from "react"; +import React, { useCallback, useEffect, useRef, useState } from "react"; import CategoryPeriodsInput from "../Components/CategoryPeriodsInput"; import CategoryPeriods from "../Models/CategoryPeriods"; import { calculateSpanDiff, filterPeriods } from "../Models/Span"; import auth from "../auth"; import { post } from "../gateway"; -export default function MembershipPeriodsInput(props) { +function MembershipPeriodsInput({ spans, member_id }) { const [showHistoric, setShowHistoric] = useState(true); const [saveDisabled, setSaveDisabled] = useState(true); - const categoryPeriodsList = [ + const categoryPeriodsListRef = useRef([ new CategoryPeriods({ category: "labaccess" }), new CategoryPeriods({ category: "membership" }), new CategoryPeriods({ category: "special_labaccess" }), - ]; + ]); - const canSave = () => { + const categoryPeriodsList = categoryPeriodsListRef.current; + + const canSave = useCallback(() => { return ( categoryPeriodsList.every((c) => c.isValid()) && categoryPeriodsList.some((c) => c.isDirty()) ); - }; + }, [categoryPeriodsList]); useEffect(() => { - const unsubscribe = []; - unsubscribe.push( - props.spans.subscribe(({ items }) => { + const unsubscribes = []; + + // Subscribe to spans updates + unsubscribes.push( + spans.subscribe(({ items }) => { categoryPeriodsList.forEach((periods) => periods.replace(filterPeriods(items, periods.category)), ); }), ); + + // Subscribe to categoryPeriods updates categoryPeriodsList.forEach((cp) => { - unsubscribe.push(cp.subscribe(() => setSaveDisabled(!canSave()))); + unsubscribes.push(cp.subscribe(() => setSaveDisabled(!canSave()))); }); return () => { - unsubscribe.forEach((u) => u()); + unsubscribes.forEach((u) => u()); }; - }, [props.spans, categoryPeriodsList]); + }, [spans, categoryPeriodsList, canSave]); - const onSave = () => { + const onSave = useCallback(() => { const deleteSpans = []; const addSpans = []; categoryPeriodsList.forEach((cp) => { cp.merge(); calculateSpanDiff({ - items: props.spans.items, + items: spans.items, categoryPeriods: cp, - member_id: props.member_id, + member_id, deleteSpans, addSpans, }); @@ -72,14 +78,22 @@ export default function MembershipPeriodsInput(props) { promises.push(...deleteSpans.map((s) => s.del())); promises.push(...addSpans.map((s) => s.save())); Promise.all(promises).then(() => { - props.spans.fetch(); + spans.fetch(); + // post({ + // url: `/webshop/member/${member_id}/ship_labaccess_orders`, + // expectedDataStatus: "ok", + // }); post({ - url: `/webshop/member/${props.member_id}/ship_labaccess_orders`, + url: `/webshop/member/${member_id}/ship_labaccess_orders`, + headers: { "Content-Type": "application/json" }, + payload: { + /* any required data */ + }, expectedDataStatus: "ok", }); }); - }; + }, [categoryPeriodsList, spans, member_id]); return (
); } + +export default MembershipPeriodsInput; From a5b9cfbf93b8fe880fc145e773db419470764e4b Mon Sep 17 00:00:00 2001 From: Shanting Hou Date: Sat, 7 Dec 2024 21:37:38 -0500 Subject: [PATCH 47/53] fix gift card network error --- admin/src/Sales/GiftCardShow.js | 39 ++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/admin/src/Sales/GiftCardShow.js b/admin/src/Sales/GiftCardShow.js index cdecd8c36..9c2561a34 100644 --- a/admin/src/Sales/GiftCardShow.js +++ b/admin/src/Sales/GiftCardShow.js @@ -1,36 +1,39 @@ import React, { useEffect, useState } from "react"; -import { Link, useParams } from "react-router-dom"; +import { Link } from "react-router-dom"; import CollectionTable from "../Components/CollectionTable"; import Currency from "../Components/Currency"; import Collection from "../Models/Collection"; import GiftCard from "../Models/GiftCard"; import GiftCardRow from "../Models/GiftCardRow"; -const GiftCardShow = () => { - const { id } = useParams(); // Get id from URL params - const [giftCardDetails, setGiftCardDetails] = useState({ - email: "", - validation_code: "", - }); +const GiftCardShow = ({ match }) => { + const { id } = match.params; + const [state, setState] = useState({}); - const gift_card = GiftCard.get(id); - const gift_cardRows = new Collection({ - type: GiftCardRow, - url: `/webshop/gift-card/${id}/products`, - pageSize: 0, - expand: "product", - }); + const gift_card = React.useMemo(() => GiftCard.get(id), [id]); + const gift_cardRows = React.useMemo( + () => + new Collection({ + type: GiftCardRow, + url: `/webshop/gift-card/${id}/products`, + pageSize: 0, + expand: "product", + }), + [id], + ); useEffect(() => { const unsubscribe = gift_card.subscribe(() => { const { email, validation_code } = gift_card; - setGiftCardDetails({ email, validation_code }); + setState({ email, validation_code }); }); - return () => unsubscribe(); // Cleanup on component unmount + return () => { + unsubscribe(); + }; }, [gift_card]); - const { email, validation_code } = giftCardDetails; + const { email, validation_code } = state; return (
@@ -57,7 +60,7 @@ const GiftCardShow = () => { { title: "Summa", class: "uk-text-right" }, ]} rowComponent={({ item }) => ( - + {item.name} From ce5f2b060940ac026b610a4adaf6e83a6411b541 Mon Sep 17 00:00:00 2001 From: Shanting Hou Date: Sat, 7 Dec 2024 21:43:51 -0500 Subject: [PATCH 48/53] fix order show --- admin/src/Sales/OrderShow.js | 51 ++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/admin/src/Sales/OrderShow.js b/admin/src/Sales/OrderShow.js index b73d0664a..2debe526e 100644 --- a/admin/src/Sales/OrderShow.js +++ b/admin/src/Sales/OrderShow.js @@ -1,5 +1,5 @@ -import React, { useEffect, useState } from "react"; -import { Link, useParams } from "react-router-dom"; +import React, { useEffect, useMemo, useState } from "react"; +import { Link } from "react-router-dom"; import CollectionTable from "../Components/CollectionTable"; import Currency from "../Components/Currency"; import Collection from "../Models/Collection"; @@ -8,22 +8,31 @@ import OrderAction from "../Models/OrderAction"; import OrderRow from "../Models/OrderRow"; import { dateTimeToStr } from "../utils"; -const OrderShow = () => { - const { id } = useParams(); // Get id from URL params - const [memberId, setMemberId] = useState(null); +const OrderShow = ({ match }) => { + const { id } = match.params; - const order = Order.get(id); - const orderRows = new Collection({ - type: OrderRow, - url: `/webshop/transaction/${id}/contents`, - pageSize: 0, - expand: "product", - }); - const orderActions = new Collection({ - type: OrderAction, - url: `/webshop/transaction/${id}/actions`, - pageSize: 0, - }); + const order = useMemo(() => Order.get(id), [id]); + const orderRows = useMemo( + () => + new Collection({ + type: OrderRow, + url: `/webshop/transaction/${id}/contents`, + pageSize: 0, + expand: "product", + }), + [id], + ); + const orderActions = useMemo( + () => + new Collection({ + type: OrderAction, + url: `/webshop/transaction/${id}/actions`, + pageSize: 0, + }), + [id], + ); + + const [memberId, setMemberId] = useState(null); useEffect(() => { const unsubscribe = order.subscribe(() => { @@ -31,7 +40,9 @@ const OrderShow = () => { setMemberId(member_id); }); - return () => unsubscribe(); // Cleanup on component unmount + return () => { + unsubscribe(); + }; }, [order]); return ( @@ -57,7 +68,7 @@ const OrderShow = () => { { title: "Summa", class: "uk-text-right" }, ]} rowComponent={({ item }) => ( - + {item.name} @@ -89,7 +100,7 @@ const OrderShow = () => { { title: "Utförd", class: "uk-text-right" }, ]} rowComponent={({ item }) => ( - + {item.id} {item.action_type} {item.value} From 7df1644be06fdd310ec461f9f2f573c3622a3d30 Mon Sep 17 00:00:00 2001 From: ilong4rennes Date: Mon, 9 Dec 2024 01:37:23 -0500 Subject: [PATCH 49/53] fix messages --- admin/src/Messages/MessageAdd.js | 17 +++++--- admin/src/Messages/MessageShow.js | 72 ++++++++++++++++++++++++------- 2 files changed, 67 insertions(+), 22 deletions(-) diff --git a/admin/src/Messages/MessageAdd.js b/admin/src/Messages/MessageAdd.js index 883c2fee6..26158fb02 100644 --- a/admin/src/Messages/MessageAdd.js +++ b/admin/src/Messages/MessageAdd.js @@ -1,16 +1,19 @@ -import React, { useState } from "react"; -import { useHistory } from "react-router-dom"; +import React, { useMemo } from "react"; +import { withRouter } from "react-router"; import MessageForm from "../Components/MessageForm"; import Message from "../Models/Message"; import { notifySuccess } from "../message"; -export default function MessageAdd() { - const [message] = useState(new Message()); - const navigate = useHistory(); +function MessageAdd(props) { + const { router } = props; + // Create a new Message instance + const message = useMemo(() => new Message(), []); + + // Handler for saving the message const onSend = () => { message.save().then(() => { - navigate("/messages"); + router.push("/messages"); notifySuccess("Ditt meddelande har skickats"); }); }; @@ -26,3 +29,5 @@ export default function MessageAdd() {
); } + +export default withRouter(MessageAdd); diff --git a/admin/src/Messages/MessageShow.js b/admin/src/Messages/MessageShow.js index 267cf829e..4f69eeed7 100644 --- a/admin/src/Messages/MessageShow.js +++ b/admin/src/Messages/MessageShow.js @@ -1,22 +1,58 @@ -import React, { useEffect, useState } from "react"; -import { useParams } from "react-router-dom"; +import React, { useEffect, useMemo, useState } from "react"; +import { withRouter } from "react-router"; import DateTimeShow from "../Components/DateTimeShow"; import Message from "../Models/Message"; -export default function MessageShow() { - const { id } = useParams(); - const [message, setMessage] = useState({}); +function MessageShow(props) { + const { id } = props.match.params; + const messageInstance = useMemo(() => Message.get(id), [id]); + + const [message, setMessage] = useState(() => { + // Initialize with extracted attributes + return { + body: "", + created_at: null, + id: null, + recipient: "", + recipient_id: null, + recipients: [], + sent_at: null, + status: "", + subject: "", + template: "", + updated_at: null, + }; + }); useEffect(() => { - const messageInstance = Message.get(id); - const unsubscribe = messageInstance.subscribe(() => { - setMessage(messageInstance); - }); + // Extract message attributes from the instance and set state + const updateMessage = () => { + setMessage({ + body: messageInstance.body, + created_at: messageInstance.created_at, + id: messageInstance.id, + recipient: messageInstance.recipient, + recipient_id: messageInstance.recipient_id, + recipients: messageInstance.recipients, + sent_at: messageInstance.sent_at, + status: messageInstance.status, + subject: messageInstance.subject, + template: messageInstance.template, + updated_at: messageInstance.updated_at, + }); + }; + // Call the function once to initialize state + updateMessage(); + + // Subscribe to updates + const unsubscribe = messageInstance.subscribe(updateMessage); + + // Cleanup subscription on unmount return () => { unsubscribe(); }; - }, [id]); + }, [messageInstance]); return (
@@ -27,7 +63,9 @@ export default function MessageShow() { Created - + @@ -37,16 +75,16 @@ export default function MessageShow() { Sent - + Recipient - {message.recipient} + {message.recipient || "N/A"} Template Used - {message.template} + {message.template || "N/A"} @@ -55,12 +93,14 @@ export default function MessageShow() {

-
+
); } + +export default withRouter(MessageShow); From 850746ee80be71abafd570a3e54a097dbf22ac77 Mon Sep 17 00:00:00 2001 From: ilong4rennes Date: Mon, 9 Dec 2024 01:54:19 -0500 Subject: [PATCH 50/53] fix span show --- admin/src/Membership/SpanShow.jsx | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/admin/src/Membership/SpanShow.jsx b/admin/src/Membership/SpanShow.jsx index 2b331a7e3..cbbaab4f6 100644 --- a/admin/src/Membership/SpanShow.jsx +++ b/admin/src/Membership/SpanShow.jsx @@ -1,19 +1,21 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import * as _ from "underscore"; import Span from "../Models/Span"; -export default function SpanShow(props) { +function SpanShow(props) { const { span_id } = props.match.params; + const spanInstance = useMemo(() => Span.get(span_id), [span_id]); const [data, setData] = useState({}); - const span = Span.get(span_id); - useEffect(() => { - const unsubscribe = span.subscribe(() => setData(span.saved)); + const unsubscribe = spanInstance.subscribe(() => { + setData(spanInstance.saved); + }); - // Cleanup subscription on unmount - return () => unsubscribe(); - }, [span]); + return () => { + unsubscribe(); + }; + }, [spanInstance]); return (
@@ -29,3 +31,5 @@ export default function SpanShow(props) {
); } + +export default SpanShow; From c58b7a8c93a126bf6f70c1575d6d9953c4451f2b Mon Sep 17 00:00:00 2001 From: ilong4rennes Date: Sat, 14 Dec 2024 00:49:01 +0900 Subject: [PATCH 51/53] refactored the 'preventDefault' error, and removed comments --- admin/src/Components/CollectionTable.js | 5 - admin/src/Components/DatePeriodInput.js | 1 - admin/src/Components/FileInput.jsx | 136 ------------------ admin/src/Components/MemberForm.js | 2 +- admin/src/Components/ProductForm.jsx | 2 +- admin/src/Components/RequestPasswordReset.jsx | 19 +-- admin/src/Components/SearchBox.jsx | 5 +- admin/src/Components/SelectInput.jsx | 1 - admin/src/Components/TextInput.jsx | 1 - admin/src/Components/Textarea.jsx | 1 - admin/src/Membership/GroupBoxEditInfo.jsx | 4 +- admin/src/Membership/KeyEdit.jsx | 6 +- 12 files changed, 18 insertions(+), 165 deletions(-) diff --git a/admin/src/Components/CollectionTable.js b/admin/src/Components/CollectionTable.js index 91137963d..7ff6ac7d4 100644 --- a/admin/src/Components/CollectionTable.js +++ b/admin/src/Components/CollectionTable.js @@ -3,13 +3,11 @@ import * as _ from "underscore"; import { confirmModal } from "../message"; const CollectionTable = (props) => { - // State variables const [sort, setSort] = useState({ key: null, order: "up" }); const [items, setItems] = useState(null); const [page, setPage] = useState({}); const [loading, setLoading] = useState(true); - // Destructure props const { collection, rowComponent, @@ -19,7 +17,6 @@ const CollectionTable = (props) => { onPageNav, } = props; - // useEffect to handle subscription useEffect(() => { const unsubscribe = collection.subscribe(({ page, items }) => { setPage(page); @@ -31,7 +28,6 @@ const CollectionTable = (props) => { }; }, [collection]); - // Functions const renderHeading = (column, i) => { const sortState = sort; @@ -127,7 +123,6 @@ const CollectionTable = (props) => { ); }; - // Render logic let rows = null; if (items !== null) { rows = items.map((item, i) => ( diff --git a/admin/src/Components/DatePeriodInput.js b/admin/src/Components/DatePeriodInput.js index cb010b05c..37b71bd15 100644 --- a/admin/src/Components/DatePeriodInput.js +++ b/admin/src/Components/DatePeriodInput.js @@ -7,7 +7,6 @@ const DatePeriodInput = ({ period }) => { const [end, setEnd] = useState(period.end || null); useEffect(() => { - // Synchronize state with period on subscription updates const unsubscribe = period.subscribe(() => { setStart(period.start); setEnd(period.end); diff --git a/admin/src/Components/FileInput.jsx b/admin/src/Components/FileInput.jsx index 28510c0ad..7c6145fd0 100644 --- a/admin/src/Components/FileInput.jsx +++ b/admin/src/Components/FileInput.jsx @@ -1,104 +1,12 @@ import React, { useState } from "react"; const FileInput = () => { - // const [progressbarVisible, setProgressbarVisible] = useState(false); - // const [progressbarWidth, setProgressbarWidth] = useState(0); const [filename, setFilename] = useState(""); - /* - var _this = this; - var settings = { - action: config.apiBasePath + this.props.action, - allow : "*.(txt|xml|csv)", - headers: { - "Authorization": "Bearer " + auth.getAccessToken() - }, - loadstart: function() - { - _this.setState({ - progressbarVisible: true, - progressbarWidth: 0, - }); - }, - - progress: function(percent) - { - _this.setState({ - progressbarWidth: Math.ceil(percent), - }); - }, - - allcomplete: function(response, xhr) - { - // Show the progress bar for another seconds - setTimeout(function() - { - _this.setState({ - progressbarVisible: false, - progressbarWidth: 0, - }); - }, 1000); - - // Fix error handling - if(xhr.status == 201) - { - // Save the filename - var result = JSON.parse(response); - _this.setState({filename: result.data.filename}); - - if(_this.props.onFile !== undefined) - { - _this.props.onFile(result.data.filename); - } - } - else - { - alert("Upload failed"); - } - - } - }; - - var select = UIkit.uploadSelect($("#upload-select"), settings), - drop = UIkit.uploadDrop($("#upload-drop"), settings); - */ - - /* - var _this = this; - - // A sync event is fired when the model is saved - // When the model is saved the field is no longer dirty - this.state.model.on("sync", function(event) - { - _this.setState({ - isDirty: _this.state.model.attributeHasChanged(_this.props.name), - }); - }); - - // Update this component when the model is changed - this.state.model.on("change", function() - { - if(_this.state.model.changed[_this.props.name] !== undefined) - { - _this.setState({ - value: _this.state.model.changed[_this.props.name], - isDirty: _this.state.model.attributeHasChanged(_this.props.name), - }); - } - }); -*/ - const clearUpload = () => { setFilename(""); }; - /* - onChange(event) - { - this.state.model.set(this.props.name, event.target.value); - } -*/ - return (
@@ -124,53 +32,9 @@ const FileInput = () => { )}

- - {/* {progressbarVisible ? ( -
-
-
- {progressbarWidth}% -
-
-
- ) : ( - "" - )} */}
); - - /* - var classes = classNames({ - "uk-form-row": true, - "selected": this.state.selected, - "changed": this.state.isDirty, - "error": this.state.error_column == this.props.name, - }); - classes += " " + this.props.name; - - return ( -
- -
- {this.props.icon ? -
- - -
- : - - } - {this.state.error_column == this.props.name ? -

{this.state.error_message}

- : ""} -
-
- ); -*/ }; export default FileInput; diff --git a/admin/src/Components/MemberForm.js b/admin/src/Components/MemberForm.js index 7d4365676..80f92bea3 100644 --- a/admin/src/Components/MemberForm.js +++ b/admin/src/Components/MemberForm.js @@ -22,8 +22,8 @@ const MemberForm = ({ member, onSave, onDelete }) => { { - e.preventDefault(); onSave(); + return false; }} >
diff --git a/admin/src/Components/ProductForm.jsx b/admin/src/Components/ProductForm.jsx index 23722ca89..c5cfe8359 100644 --- a/admin/src/Components/ProductForm.jsx +++ b/admin/src/Components/ProductForm.jsx @@ -97,8 +97,8 @@ const ProductForm = ({ product, onDelete, onSave }) => { { - e.preventDefault(); onSave(); + return false; }} >
diff --git a/admin/src/Components/RequestPasswordReset.jsx b/admin/src/Components/RequestPasswordReset.jsx index c072e9cbe..5835ceaf3 100644 --- a/admin/src/Components/RequestPasswordReset.jsx +++ b/admin/src/Components/RequestPasswordReset.jsx @@ -1,24 +1,26 @@ -import React, { useRef } from "react"; -import { withRouter } from "react-router"; // Retain withRouter for compatibility +import React, { useState } from "react"; +import { withRouter } from "react-router"; import auth from "../auth"; import { browserHistory } from "../browser_history"; import { showError, showSuccess } from "../message"; const RequestPasswordReset = () => { - const userIdentificationRef = useRef(null); + const [userIdentification, setUserIdentification] = useState(""); + + const handleInputChange = (e) => { + setUserIdentification(e.target.value); + }; const submit = (e) => { e.preventDefault(); - const user_identification = userIdentificationRef.current.value; - // Error handling - if (!user_identification) { + if (!userIdentification) { showError("You need to fill your email or member number."); return; } - auth.requestPasswordReset(user_identification).then(() => { + auth.requestPasswordReset(userIdentification).then(() => { showSuccess( "Link to password reset will be sent to your email shortly.", ); @@ -53,7 +55,8 @@ const RequestPasswordReset = () => {
{ - const searchRef = useRef(null); - return (
@@ -15,7 +13,6 @@ const SearchBox = ({ value, handleChange }) => { { }; const unsubscribe = props.model.subscribe(handleModelChange); - handleModelChange(); // Initialize state return () => { unsubscribe(); diff --git a/admin/src/Components/TextInput.jsx b/admin/src/Components/TextInput.jsx index caae1c8e3..611a7bb5a 100644 --- a/admin/src/Components/TextInput.jsx +++ b/admin/src/Components/TextInput.jsx @@ -16,7 +16,6 @@ const TextInput = (props) => { }; const unsubscribe = model.subscribe(handleModelChange); - handleModelChange(); // Initialize state return () => { unsubscribe(); diff --git a/admin/src/Components/Textarea.jsx b/admin/src/Components/Textarea.jsx index fb8e6feef..fd03142cc 100644 --- a/admin/src/Components/Textarea.jsx +++ b/admin/src/Components/Textarea.jsx @@ -15,7 +15,6 @@ const Textarea = (props) => { }; const unsubscribe = model.subscribe(handleModelChange); - handleModelChange(); // Initialize state return () => { unsubscribe(); diff --git a/admin/src/Membership/GroupBoxEditInfo.jsx b/admin/src/Membership/GroupBoxEditInfo.jsx index 62c2e7069..49c4c339f 100644 --- a/admin/src/Membership/GroupBoxEditInfo.jsx +++ b/admin/src/Membership/GroupBoxEditInfo.jsx @@ -6,10 +6,10 @@ import { confirmModal } from "../message"; const GroupBoxEditInfo = (props) => { const { router } = props; - const group = useContext(GroupContext); // Access the group from context + const group = useContext(GroupContext); if (!group) { - return
Group not found
; // Handle missing context gracefully + return
Group not found
; } return ( diff --git a/admin/src/Membership/KeyEdit.jsx b/admin/src/Membership/KeyEdit.jsx index 1560b488d..798461d39 100644 --- a/admin/src/Membership/KeyEdit.jsx +++ b/admin/src/Membership/KeyEdit.jsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useRef, useState } from "react"; -import { useParams, withRouter } from "react-router"; +import { useParams } from "react-router"; import DateTimeInput from "../Components/DateTimeInput"; import TextInput from "../Components/TextInput"; import Textarea from "../Components/Textarea"; @@ -53,7 +53,6 @@ function KeyEdit() { model={keyRef.current} name="created_at" title="Skapad" - disabled={true} />
@@ -61,7 +60,6 @@ function KeyEdit() { model={keyRef.current} name="updated_at" title="Ändrad" - disabled={true} />
@@ -104,4 +102,4 @@ function KeyEdit() { ); } -export default withRouter(KeyEdit); +export default KeyEdit; From ef57643a84ef96077774348b354a595ebd6329c4 Mon Sep 17 00:00:00 2001 From: ilong4rennes Date: Sat, 14 Dec 2024 01:13:20 +0900 Subject: [PATCH 52/53] Remove useCallback --- admin/src/Components/MessageForm.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/admin/src/Components/MessageForm.jsx b/admin/src/Components/MessageForm.jsx index afa90f313..62436da90 100644 --- a/admin/src/Components/MessageForm.jsx +++ b/admin/src/Components/MessageForm.jsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; import { Async } from "react-select"; import { get } from "../gateway"; import Group from "../Models/Group"; @@ -46,7 +46,7 @@ const MessageForm = ({ message, onSave, recipientSelect }) => { }; }, [message]); - const loadOptions = useCallback((inputValue, callback) => { + const loadOptions = (inputValue, callback) => { Promise.all([ get({ url: "/membership/group", @@ -71,7 +71,7 @@ const MessageForm = ({ message, onSave, recipientSelect }) => { .concat(members.map((d) => memberOption(d))), ), ); - }, []); + }; const handleSubmit = (e) => { e.preventDefault(); From 577b374804f36c068f351b40db78ddb9a8e87d3d Mon Sep 17 00:00:00 2001 From: ilong4rennes Date: Sat, 14 Dec 2024 01:19:43 +0900 Subject: [PATCH 53/53] Remove useCallback --- admin/src/Components/ProductForm.jsx | 49 +++++++++++++--------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/admin/src/Components/ProductForm.jsx b/admin/src/Components/ProductForm.jsx index c5cfe8359..2c2f515a8 100644 --- a/admin/src/Components/ProductForm.jsx +++ b/admin/src/Components/ProductForm.jsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; import ReactSelect from "react-select"; import ProductAction, { ACTION_TYPES } from "../Models/ProductAction"; import CheckboxInput from "./CheckboxInput"; @@ -62,32 +62,29 @@ const ProductForm = ({ product, onDelete, onSave }) => { product.removeAction(action); }; - const renderAction = useCallback( - (action) => ( -
-
{action.action_type}
-
- Värde -
-
- -
- + const renderAction = (action) => ( +
+
{action.action_type}
+
+ Värde
- ), - [], +
+ +
+ +
); const imageSrc = (o) => `data:${o.type};base64, ` + o.data;