diff --git a/src/core/appUrls.ts b/src/core/appUrls.ts index 49cef206..30cc9588 100644 --- a/src/core/appUrls.ts +++ b/src/core/appUrls.ts @@ -15,7 +15,6 @@ export const getLoginPageUrl = () => url`/login`; export const getMyProfileUrl = () => url`/profile`; export const getOfflinesUrl = () => url`/offlines`; export const getPaymentEventUrl = (eventId: number) => url`/payments/events/${{ eventId }}`; -export const getPaymentWalletUrl = () => url`/payments/wallet`; export const getPaymentWebshopUrl = () => url`/payments/webshop`; export const getPublicProfileUrl = (userId: number) => url`/profile/public/${{ userId }}`; export const getProfileSettingsUrl = () => url`/profile/settings`; @@ -30,6 +29,5 @@ export const getProfileCalendarUrl = () => url`/profile/settings/calendar`; export const getProfileMembershipUrl = () => url`/profile/settings/membership`; export const getProfileStatisticsUrl = () => url`/profile/statistics`; export const getProfileStatisticsEventsUrl = () => url`/profile/statistics/events`; -export const getProfileStatisticsOrdersUrl = () => url`/profile/statistics/orders`; export const getProfileSearchUrl = () => url`/profile/search`; export const getResourcesUrl = () => url`/resources`; diff --git a/src/core/components/Header/Login.tsx b/src/core/components/Header/Login.tsx index ce566816..452fe9e1 100644 --- a/src/core/components/Header/Login.tsx +++ b/src/core/components/Header/Login.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { logOut } from 'authentication/api'; import LoginView from 'authentication/components/Login'; -import { getMyProfileUrl, getPaymentWalletUrl } from 'core/appUrls'; +import { getMyProfileUrl } from 'core/appUrls'; import { Link } from 'core/components/Router'; import style from './header.less'; @@ -79,9 +79,6 @@ const HeaderUser = (props: IHeaderUserProps) => { )} - - Saldo - Kontakt oss diff --git a/src/core/redux/Store.tsx b/src/core/redux/Store.tsx index 2fd7e4ae..1874222e 100644 --- a/src/core/redux/Store.tsx +++ b/src/core/redux/Store.tsx @@ -13,7 +13,6 @@ import { ruleBundlesReducer } from 'events/slices/ruleBundles'; import { onlineGroupsReducer } from 'groups/slices/onlineGroups'; import { transactionsReducer } from 'payments/reducers/transactions'; import { paymentsReducer } from 'payments/slices/payments'; -import { shopReducer } from 'shop/reducers'; import { publicAttendeesReducer } from 'events/slices/publicAttendees'; import { notificationMessagesReducer } from 'notifications/slices/notifications'; import { notificationPermissionsReducer } from 'notifications/slices/permissions'; @@ -42,7 +41,6 @@ export const initStore = (initialState: {} = {}) => { payments: paymentsReducer, publicAttendees: publicAttendeesReducer, ruleBundles: ruleBundlesReducer, - shop: shopReducer, transactions: transactionsReducer, }, /* eslint sort-keys: "off" */ diff --git a/src/pages/payments/wallet.tsx b/src/pages/payments/wallet.tsx deleted file mode 100644 index 21f9f7e4..00000000 --- a/src/pages/payments/wallet.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; - -import { Wallet } from 'payments/components/Wallet'; -import { useSelector } from 'core/redux/hooks'; -import { selectIsLoggedIn } from 'authentication/selectors/authentication'; - -const PaymentWalletPage = () => { - const isLoggedIn = useSelector(selectIsLoggedIn()); - /** Should not be able to render this page without an authenticated user */ - if (!isLoggedIn) { - return

Du må logge inn med brukeren din

; - } - - return ; -}; - -export default PaymentWalletPage; diff --git a/src/pages/profile/statistics/orders.tsx b/src/pages/profile/statistics/orders.tsx deleted file mode 100644 index fbe881b9..00000000 --- a/src/pages/profile/statistics/orders.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -import { ProfileWrapper } from 'profile'; -import OrderStatistics from 'profile/components/Statistics/Orders'; - -const OrderStatisticsPage = () => { - return ( - - - - ); -}; - -export default OrderStatisticsPage; diff --git a/src/payments/components/Transactions/CreateTransaction/SaldoSelect/index.tsx b/src/payments/components/Transactions/CreateTransaction/SaldoSelect/index.tsx deleted file mode 100644 index c636ab13..00000000 --- a/src/payments/components/Transactions/CreateTransaction/SaldoSelect/index.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, { ChangeEvent, FC } from 'react'; - -import style from './select.less'; - -export const SALDO_VALUES = [100, 200, 300, 500, 800, 1000]; -export const DEFAULT_SALDO_VALUE = SALDO_VALUES[1]; - -export interface IProps { - onChange: (amount: number) => void; - selected: number; -} - -export const SaldoSelect: FC = ({ onChange, selected }) => { - const handleChange = (event: ChangeEvent) => { - onChange(Number(event.target.value)); - }; - - return ( - - ); -}; diff --git a/src/payments/components/Transactions/CreateTransaction/SaldoSelect/select.less b/src/payments/components/Transactions/CreateTransaction/SaldoSelect/select.less deleted file mode 100644 index c19dcc5e..00000000 --- a/src/payments/components/Transactions/CreateTransaction/SaldoSelect/select.less +++ /dev/null @@ -1,12 +0,0 @@ -@import '~common/less/constants.less'; - -@import '../input.less'; - -.amountSelect { - .inputStyleMixin(); - max-width: 400px; - - @media screen and (max-width: @owMobileBreakpoint) { - max-width: 100%; - } -} diff --git a/src/payments/components/Transactions/CreateTransaction/StripeForm.tsx b/src/payments/components/Transactions/CreateTransaction/StripeForm.tsx deleted file mode 100644 index 3917944f..00000000 --- a/src/payments/components/Transactions/CreateTransaction/StripeForm.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import React, { FC, useCallback, useContext, useState } from 'react'; -import { injectStripe, ReactStripeElements } from 'react-stripe-elements'; - -import { md } from 'common/components/Markdown'; -import { StatusMessage } from 'common/components/StatusMessage'; -import { useDispatch } from 'core/redux/hooks'; -import { useToast } from 'core/utils/toast/useToast'; -import { - createPaymentMethod, - createTransaction, - handleCardVerification, - IGenericReturn, -} from 'payments/api/paymentTransaction'; -import { fetchTransactions } from 'payments/reducers/transactions'; -import { UserProfileContext } from 'profile/providers/UserProfile'; -import { CardPayment } from './CardPayment'; -import style from './createTransaction.less'; -import { PaymentRequestButton } from './PaymentRequestButton'; -import { DEFAULT_SALDO_VALUE, SaldoSelect } from './SaldoSelect'; - -const ABOUT_CREATE_TRANSACTION = md` -## Legg til Saldo -`; - -export type IProps = ReactStripeElements.InjectedStripeProps; - -export const Form: FC = ({ stripe }) => { - const [displayError] = useToast({ type: 'error', duration: 12000 }); - const [displayMessage] = useToast({ duration: 12000, overwrite: true }); - const [processing, setProcessing] = useState(false); - const [amount, setAmount] = useState(DEFAULT_SALDO_VALUE); - const [finished, setFinished] = useState(false); - const dispatch = useDispatch(); - const { refetch, user } = useContext(UserProfileContext); - - const updateTransactions = useCallback(() => { - dispatch(fetchTransactions()); - }, [dispatch]); - - const USER_BALANCE = md` - # Saldo: **${String(!!user ? user.saldo : 0)} kr** - `; - - /** Handle payment statuses and display messages apropriatly to the user. */ - const handleResponse = ({ status, message }: IGenericReturn) => { - if (status === 'error') { - displayError(message); - } else if (status === 'success' || status === 'pending') { - displayMessage(message); - } - }; - - /** - * Handle creation of a payment method. Send payment data to the server. - * Used by Card payments and Payment Request payments. - */ - const handlePaymentMethod = async (paymentMethod: {}): Promise => { - if (stripe) { - const transactionResponse = await createTransaction(amount, paymentMethod); - handleResponse(transactionResponse); - if (transactionResponse.status === 'pending' && transactionResponse.transaction) { - const verifyResponse = await handleCardVerification(stripe, transactionResponse.transaction); - handleResponse(verifyResponse); - updateTransactions(); - return verifyResponse.status; - } else { - return transactionResponse.status; - } - } - return 'error'; - }; - - /** Handle submit from the CardPayment Form */ - const handleSubmit = async () => { - if (!stripe) { - displayError('Det skjedde noe galt med koblingen til betalingssystemet.'); - return; - } - setProcessing(true); - const methodResponse = await createPaymentMethod(stripe); - - handleResponse(methodResponse); - if (methodResponse.status === 'success' && methodResponse.paymentMethod) { - const status = await handlePaymentMethod(methodResponse.paymentMethod); - if (status === 'success') { - setFinished(true); - } - } - setProcessing(false); - updateTransactions(); - refetch(); - }; - - return ( -
- {USER_BALANCE} - {finished ? ( - <> - Transaksjonen var vellykket. - - - ) : ( - <> - {ABOUT_CREATE_TRANSACTION} - -
- -
- {stripe ? ( - - ) : null} -
- - )} -
- ); -}; - -export const StripeForm = injectStripe(Form); diff --git a/src/payments/components/Transactions/CreateTransaction/index.tsx b/src/payments/components/Transactions/CreateTransaction/index.tsx deleted file mode 100644 index d81e6ff0..00000000 --- a/src/payments/components/Transactions/CreateTransaction/index.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { Elements, StripeProvider } from 'react-stripe-elements'; - -import { STRIPE_KEY_TRIKOM } from 'common/constants/stripe'; -import { useStripeInit } from 'payments/hooks/useStripeInit'; - -import { StripeForm } from './StripeForm'; - -export const CreateTransaction = () => { - const stripe = useStripeInit(STRIPE_KEY_TRIKOM); - - return ( - - - - - - ); -}; diff --git a/src/payments/components/Transactions/Purchases.tsx b/src/payments/components/Transactions/Purchases.tsx deleted file mode 100644 index 74803aab..00000000 --- a/src/payments/components/Transactions/Purchases.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { DateTime } from 'luxon'; -import React, { FC, useEffect } from 'react'; - -import Spinner from 'common/components/Spinner'; -import { DataTable, DataTableHeaders, DataTableSorters } from 'common/components/Table/DataTable'; -import { useDispatch, useSelector } from 'core/redux/hooks'; -import { useToast } from 'core/utils/toast/useToast'; -import { transformOrderData } from 'profile/api/orders'; -import { fetchOrderLines } from 'shop/reducers/orderLines'; - -import { Paid } from '../Paid'; -import style from './transactions.less'; - -export interface IOrderData { - product: string; - paid: boolean; - amount: number; - date: DateTime; - price: number; -} - -const tableHeaders: DataTableHeaders = { - product: 'Produkt', - price: 'Pris', - amount: 'Antall', - paid: 'Betalt', - date: 'Dato', -}; - -const tableSorters: DataTableSorters = { - product: (a, b) => b.product.localeCompare(a.product), - amount: (a, b) => b.amount - a.amount, - date: (a, b) => b.date.toMillis() - a.date.toMillis(), - paid: (a, b) => Number(b.paid) - Number(a.paid), - price: (a, b) => b.price - a.price, -}; - -export const Purchases: FC = () => { - const [displayMessage] = useToast(); - const dispatch = useDispatch(); - const errors = useSelector((state) => state.shop.orderLines.errors); - const orders = useSelector((state) => transformOrderData(state.shop.orderLines.orderLines)); - const status = useSelector((state) => state.shop.orderLines.status); - - const init = () => { - dispatch(fetchOrderLines()); - }; - - useEffect(() => { - init(); - }, []); - - useEffect(() => { - if (errors) { - displayMessage(String(errors)); - } - }, [errors]); - - return ( -
- {status === 'ready' ? ( - - {(items) => items.map((order) => )} - - ) : ( - - )} -
- ); -}; - -export interface IOrderlineProps { - order: IOrderData; -} - -const Order: FC = ({ order }) => { - const formattedDate = order.date.toLocaleString(DateTime.DATETIME_MED); - return ( - - {order.product} - {`${Number(order.price)} kr`} - {order.amount} - {} - {formattedDate} - - ); -}; diff --git a/src/payments/components/Transactions/Transaction.tsx b/src/payments/components/Transactions/Transaction.tsx deleted file mode 100644 index 63673eef..00000000 --- a/src/payments/components/Transactions/Transaction.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { DateTime } from 'luxon'; -import React, { FC } from 'react'; - -import { IPaymentTransaction } from 'payments/models/PaymentTransaction'; - -import { Status } from '../Status'; -import style from './transactions.less'; -import { TypeIcon } from './TypeIcon'; - -export interface IProps { - transaction: IPaymentTransaction; -} - -export const Transaction: FC = ({ transaction }) => { - const formattedDate = DateTime.fromISO(transaction.datetime).toLocaleString(DateTime.DATETIME_MED); - return ( - - {formattedDate} - {`${transaction.amount} kr`} - - - - -
- -
- - - ); -}; diff --git a/src/payments/components/Transactions/TypeIcon.tsx b/src/payments/components/Transactions/TypeIcon.tsx deleted file mode 100644 index ed501705..00000000 --- a/src/payments/components/Transactions/TypeIcon.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { faStripe } from '@fortawesome/free-brands-svg-icons/faStripe'; -import { faMoneyBillAlt } from '@fortawesome/free-regular-svg-icons/faMoneyBillAlt'; -import { FontAwesomeIcon as Icon } from '@fortawesome/react-fontawesome'; -import React, { FC } from 'react'; - -import { PaymentType } from 'payments/models/Payment'; - -export interface IProps { - type: PaymentType; -} - -const getPaymentTypeIcon = (type: PaymentType) => { - switch (type) { - case 'cash': - return ; - case 'stripe': - return ; - } -}; - -export const TypeIcon: FC = ({ type }) => { - return getPaymentTypeIcon(type); -}; diff --git a/src/payments/components/Transactions/index.tsx b/src/payments/components/Transactions/index.tsx deleted file mode 100644 index b76f7c59..00000000 --- a/src/payments/components/Transactions/index.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { FC, useEffect } from 'react'; - -import Spinner from 'common/components/Spinner'; -import { DataTable, DataTableHeaders, DataTableSorters } from 'common/components/Table/DataTable'; -import { useDispatch, useSelector } from 'core/redux/hooks'; -import { useToast } from 'core/utils/toast/useToast'; -import { getAllTransactions } from 'payments/api/paymentTransaction'; -import { PaymentStatus } from 'payments/models/Payment'; -import { IPaymentTransaction } from 'payments/models/PaymentTransaction'; -import { Type } from 'payments/reducers/transactions'; - -import { Transaction } from './Transaction'; -import style from './transactions.less'; - -const tableHeaders: DataTableHeaders = { - datetime: 'Dato', - amount: 'Verdi', - status: 'Betalt', - used_stripe: 'Betalingsløsning', -}; - -const STATUS_SORT_ORDER: PaymentStatus[] = ['pending', 'succeeded', 'done', 'refunded', 'removed']; - -const tableSorters: DataTableSorters = { - datetime: (a, b) => Date.parse(b.datetime) - Date.parse(a.datetime), - amount: (a, b) => b.amount - a.amount, - used_stripe: (a, b) => Number(b.used_stripe) - Number(a.used_stripe), - status: (a, b) => STATUS_SORT_ORDER.indexOf(b.status) - STATUS_SORT_ORDER.indexOf(a.status), -}; - -export const Transactions: FC = () => { - const [addMessage] = useToast({ type: 'error', duration: 10000 }); - - const dispatch = useDispatch(); - const transactions = useSelector((state) => state.transactions.transactions); - const fetching = useSelector((state) => state.transactions.fetching); - - const fetchTransactions = async () => { - dispatch({ type: Type.SET_FETCHING, status: true }); - try { - const data = await getAllTransactions(); - dispatch({ - type: Type.SET_TRANSACTIONS, - transactions: data, - }); - } catch (err) { - addMessage(String(err)); - } - dispatch({ type: Type.SET_FETCHING, status: false }); - }; - - useEffect(() => { - fetchTransactions(); - }, []); - - return ( -
- {transactions.length === 0 && fetching ? ( - - ) : ( - - {(items) => items.map((transaction) => )} - - )} -
- ); -}; diff --git a/src/payments/components/Transactions/transactions.less b/src/payments/components/Transactions/transactions.less deleted file mode 100644 index ee229362..00000000 --- a/src/payments/components/Transactions/transactions.less +++ /dev/null @@ -1,24 +0,0 @@ -@import '~common/less/colors.less'; -@import '~common/less/mixins.less'; -@import '~common/less/constants.less'; - -.transactionDetails { - margin-top: 10px; - display: flex; - justify-content: space-between; -} - -.transactionsContainer { - display: grid; - gap: 12px; -} - -.centerText { - text-align: center; - vertical-align: middle; -} - -.centerIcon { - display: flex; - justify-content: center; -} diff --git a/src/payments/components/Wallet/index.tsx b/src/payments/components/Wallet/index.tsx deleted file mode 100644 index 88226d58..00000000 --- a/src/payments/components/Wallet/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React, { FC, useContext } from 'react'; - -import { md } from 'common/components/Markdown'; -import { UserProfileContext } from 'profile/providers/UserProfile'; -import { Page, Pane } from 'common/components/Panes'; - -import { Transactions } from 'payments/components/Transactions'; -import { Purchases } from 'payments/components/Transactions/Purchases'; -export const Wallet: FC = () => { - const { user } = useContext(UserProfileContext); - - const ABOUT_TRANSACTIONS = md` -# Vi bruker nå vipps i kiosken! 🔥 -Nå har vi endelig gått over til ny betalingsløsning i kiosken hvor man betaler ved Vipps! - -**Hvordan betaler jeg?** 😎 - -Scan qr koden i kiosken eller på kjøleskapet, velg produkter og betal med Vipps. Første gangen du kjøper noe må du verifisere Vipps før du betaler, så dobbeltsjekk at Vipps-betalingen går gjennom. - -**Men jeg har fortsatt penger på studentkortet mitt** 😱 - -Frykt ikke! Vi vil ha en overgangsperiode på 2 måneder frem til **1. november**, så dere har god tid på å bruke opp saldoen deres! Send melding om dette ikke går så skal vi finne en løsning. - -Send melding til Johanna Wilmers på slack hvis det er noen spørsmål/problemer - -## Gjennværende saldo: **${String(!!user ? user.saldo : 0)} kr** - `; - - return ( - - {ABOUT_TRANSACTIONS} - - - - ); -}; diff --git a/src/profile/api/orders.ts b/src/profile/api/orders.ts deleted file mode 100644 index de5007ec..00000000 --- a/src/profile/api/orders.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { DateTime } from 'luxon'; - -import { IAuthUser } from 'authentication/models/User'; -import { getAllPages } from 'common/utils/api'; -import { IOrderData } from 'payments/components/Transactions/Purchases'; -import { IOrderLine } from 'shop/models'; - -const API_URL = '/api/v1/profile/orders/'; - -export const transformOrderData = (orderLines: IOrderLine[]): IOrderData[] => { - const orderData: IOrderData[] = []; - for (const orderLine of orderLines) { - for (const data of orderLine.orders) { - orderData.push({ - amount: data.quantity, - date: DateTime.fromISO(orderLine.datetime), - paid: orderLine.paid, - price: data.price, - product: data.content_object?.name || 'Fjernet vare', - }); - } - } - return orderData; -}; - -export const getOrders = async (user: IAuthUser): Promise => { - const results = await getAllPages(API_URL, { page_size: 80 }, { user }); - return results; -}; diff --git a/src/profile/components/Statistics/Events/CompanyDonut.tsx b/src/profile/components/Statistics/Events/CompanyDonut.tsx index 6fb12dec..68e7f115 100644 --- a/src/profile/components/Statistics/Events/CompanyDonut.tsx +++ b/src/profile/components/Statistics/Events/CompanyDonut.tsx @@ -1,7 +1,7 @@ import { Pie } from '@nivo/pie'; import { IEvent } from 'events/models/Event'; import React from 'react'; -import style from '../Orders/orders.less'; +import style from './events.less'; export interface ICompanyCount { [key: string]: number; diff --git a/src/profile/components/Statistics/Events/EventTypeDonut.tsx b/src/profile/components/Statistics/Events/EventTypeDonut.tsx index 73f72127..9e9875e9 100644 --- a/src/profile/components/Statistics/Events/EventTypeDonut.tsx +++ b/src/profile/components/Statistics/Events/EventTypeDonut.tsx @@ -1,7 +1,7 @@ import { Pie, PieDatum } from '@nivo/pie'; import { getEventColor, getEventType, IEvent } from 'events/models/Event'; import React from 'react'; -import style from '../Orders/orders.less'; +import style from './events.less'; export interface IProps { events: IEvent[]; diff --git a/src/profile/components/Statistics/Events/events.less b/src/profile/components/Statistics/Events/events.less index d01de1f3..68b0e7c9 100644 --- a/src/profile/components/Statistics/Events/events.less +++ b/src/profile/components/Statistics/Events/events.less @@ -1,4 +1,5 @@ @import '~common/less/colors.less'; +@import '~common/less/constants.less'; .stringStat { text-align: center; @@ -30,3 +31,28 @@ text-align: center; } } + +.centerChart { + & > div, + h1 { + display: flex; + justify-content: center; + } +} + +.barChart { + height: 550px; + max-width: 90vw; +} + +.pieChart { + width: 450px; + height: 350px; + max-width: 90vw; + margin: auto; + + @media screen and (max-width: @owMobileBreakpoint) { + width: 400px; + height: 300px; + } +} diff --git a/src/profile/components/Statistics/Events/index.tsx b/src/profile/components/Statistics/Events/index.tsx index a62ce4d4..41bb0e9c 100644 --- a/src/profile/components/Statistics/Events/index.tsx +++ b/src/profile/components/Statistics/Events/index.tsx @@ -4,7 +4,7 @@ import { getAllEvents } from 'events/api/events'; import { IEvent } from 'events/models/Event'; import { DateTime } from 'luxon'; import React, { Component } from 'react'; -import NumberStat from '../Orders/NumberStat'; +import NumberStat from '../NumberStat'; import CompanyDonut, { countCompanies } from './CompanyDonut'; import EventTypeDonut from './EventTypeDonut'; import StringStat from './StringStat'; diff --git a/src/profile/components/Statistics/Main.tsx b/src/profile/components/Statistics/Main.tsx index d09e4af8..3b250426 100644 --- a/src/profile/components/Statistics/Main.tsx +++ b/src/profile/components/Statistics/Main.tsx @@ -2,7 +2,7 @@ import React from 'react'; import Markdown from 'common/components/Markdown'; import { FourSplitPane, Page, Pane, SplitPane } from 'common/components/Panes'; -import { getProfileStatisticsEventsUrl, getProfileStatisticsOrdersUrl } from 'core/appUrls'; +import { getProfileStatisticsEventsUrl } from 'core/appUrls'; import { Link } from 'core/components/Router'; import StringStat from './Events/StringStat'; @@ -23,13 +23,6 @@ const Main = () => { - - - - - - - diff --git a/src/profile/components/Statistics/Orders/NumberStat.tsx b/src/profile/components/Statistics/NumberStat.tsx similarity index 87% rename from src/profile/components/Statistics/Orders/NumberStat.tsx rename to src/profile/components/Statistics/NumberStat.tsx index c24d319e..f6ca1f1f 100644 --- a/src/profile/components/Statistics/Orders/NumberStat.tsx +++ b/src/profile/components/Statistics/NumberStat.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import style from './orders.less'; +import style from './numberStat.less'; export interface IProps { name: string; diff --git a/src/profile/components/Statistics/Orders/AccountBalance.tsx b/src/profile/components/Statistics/Orders/AccountBalance.tsx deleted file mode 100644 index f7bc41c6..00000000 --- a/src/profile/components/Statistics/Orders/AccountBalance.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React, { useContext } from 'react'; - -import { UserProfileContext } from 'profile/providers/UserProfile'; - -import NumberStat from './NumberStat'; - -export const AccountBalance = () => { - const { user } = useContext(UserProfileContext); - return user ? : null; -}; diff --git a/src/profile/components/Statistics/Orders/OrderBar.tsx b/src/profile/components/Statistics/Orders/OrderBar.tsx deleted file mode 100644 index 5c2d38e4..00000000 --- a/src/profile/components/Statistics/Orders/OrderBar.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { BarExtendedDatum, ResponsiveBar } from '@nivo/bar'; -import classNames from 'classnames'; -import React from 'react'; - -import { LIGHT_EVENT_COLORS } from 'events/models/Event'; -import { IOrder, IOrderLine } from 'shop/models'; - -import style from './orders.less'; - -export interface IProps { - orderLines: IOrderLine[]; -} - -const Tooltip = ({ value, indexValue }: BarExtendedDatum) => ( - - {indexValue} - {value} - -); - -const OrderBar = ({ orderLines }: IProps) => { - const orders: IOrder[] = orderLines.reduce((prev, curr) => [...prev, ...curr.orders], []); - const items = orders.reduce<{ [name: string]: number }>((prev, curr) => { - if (!curr.content_object) { - return prev; - } - const name = curr.content_object.name.replace('[Discontinued] ', ''); - const prevValues = prev.hasOwnProperty(name) ? prev[name] + curr.quantity : 1; - return { ...prev, [name]: prevValues }; - }, {}); - const values = Object.keys(items).map((name) => ({ id: name, label: name, value: items[name] })); - - return ( -
-

Varefordeling

- -
- ); -}; - -export default OrderBar; diff --git a/src/profile/components/Statistics/Orders/OrderItemDonut.tsx b/src/profile/components/Statistics/Orders/OrderItemDonut.tsx deleted file mode 100644 index 74071799..00000000 --- a/src/profile/components/Statistics/Orders/OrderItemDonut.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { ResponsivePie } from '@nivo/pie'; -import classnames from 'classnames'; -import React from 'react'; - -import { LIGHT_EVENT_COLORS } from 'events/models/Event'; -import { IOrder, IOrderLine } from 'shop/models'; - -import style from './orders.less'; - -export interface IProps { - orderLines: IOrderLine[]; -} - -const OrderItemDonut = ({ orderLines }: IProps) => { - /** TODO: Allow flatMap */ - const orders: IOrder[] = orderLines.reduce((prev, curr) => [...prev, ...curr.orders], []); - const categories = orders.reduce<{ [name: string]: number }>((prev, curr) => { - if (!curr.content_object) { - return prev; - } - const { name } = curr.content_object.category; - const prevValues = prev.hasOwnProperty(name) ? prev[name] + curr.quantity : 1; - return { ...prev, [name]: prevValues }; - }, {}); - const values = Object.keys(categories).map((name) => ({ id: name, label: name, value: categories[name] })); - - return ( -
-

Kategorier

- -
- ); -}; - -export default OrderItemDonut; diff --git a/src/profile/components/Statistics/Orders/index.tsx b/src/profile/components/Statistics/Orders/index.tsx deleted file mode 100644 index c4adcd76..00000000 --- a/src/profile/components/Statistics/Orders/index.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { DateTime } from 'luxon'; -import React, { FC, useEffect } from 'react'; - -import CalendarChart from 'common/components/Charts/CalendarChart'; -import { FourSplitPane, Page, Pane, SplitPane } from 'common/components/Panes'; -import { useDispatch, useSelector } from 'core/redux/hooks'; -import { IOrder } from 'shop/models'; -import { fetchOrderLines } from 'shop/reducers/orderLines'; - -import { AccountBalance } from './AccountBalance'; -import NumberStat from './NumberStat'; -import OrderBar from './OrderBar'; -import OrderItemDonut from './OrderItemDonut'; - -export const Orders: FC = () => { - const dispatch = useDispatch(); - const orderLines = useSelector((state) => state.shop.orderLines.orderLines); - - const init = () => { - dispatch(fetchOrderLines()); - }; - - const orders = orderLines.reduce((prev, curr) => [...prev, ...curr.orders], []); - const frequency = orderLines.map((line) => DateTime.fromISO(line.datetime)).sort(); - const totalOrderLines = orderLines.length; - const totalItems = orders.reduce((acc, order) => acc + order.quantity, 0); - const totalCost = orders.reduce((acc, order) => acc + Number(order.price), 0); - - useEffect(() => { - init(); - }, []); - - return ( - - {orderLines.length && } - - {orderLines.length && } - - - - - - - - - - - - - - - - {frequency.length && } - - ); -}; - -export default Orders; diff --git a/src/profile/components/Statistics/Orders/orders.less b/src/profile/components/Statistics/Orders/orders.less deleted file mode 100644 index 8040b3b9..00000000 --- a/src/profile/components/Statistics/Orders/orders.less +++ /dev/null @@ -1,45 +0,0 @@ -@import '~common/less/colors.less'; -@import '~common/less/constants.less'; - -.numberStat { - text-align: center; - & > h2 { - color: @gray; - margin-bottom: 1rem; - - &:empty { - display: none; - } - } - - & > p { - color: @bedpres; - font-size: 56px; - font-weight: bolder; - } -} - -.centerChart { - & > div, - h1 { - display: flex; - justify-content: center; - } -} - -.barChart { - height: 550px; - max-width: 90vw; -} - -.pieChart { - width: 450px; - height: 350px; - max-width: 90vw; - margin: auto; - - @media screen and (max-width: @owMobileBreakpoint) { - width: 400px; - height: 300px; - } -} diff --git a/src/profile/components/Statistics/numberStat.less b/src/profile/components/Statistics/numberStat.less new file mode 100644 index 00000000..81ab05d7 --- /dev/null +++ b/src/profile/components/Statistics/numberStat.less @@ -0,0 +1,20 @@ +@import '~common/less/colors.less'; +@import '~common/less/constants.less'; + +.numberStat { + text-align: center; + & > h2 { + color: @gray; + margin-bottom: 1rem; + + &:empty { + display: none; + } + } + + & > p { + color: @bedpres; + font-size: 56px; + font-weight: bolder; + } +} diff --git a/src/shop/models/index.ts b/src/shop/models/index.ts deleted file mode 100644 index d15af958..00000000 --- a/src/shop/models/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -import IResponsiveImage from 'common/models/ResponsiveImage'; - -export interface IOrderLine { - paid: boolean; - datetime: string; - orders: IOrder[]; -} - -export interface IOrder { - price: number; - quantity: number; - content_object: IStoreItem | undefined; -} - -export interface IStoreItem { - name: string; - price: number; - description: string | null; - image: IResponsiveImage; - category: IStoreItemCategory; -} - -export interface IStoreItemCategory { - pk: number; - name: string; -} diff --git a/src/shop/reducers/index.ts b/src/shop/reducers/index.ts deleted file mode 100644 index e65007ac..00000000 --- a/src/shop/reducers/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { combineReducers } from 'redux'; - -import { OrderLineAction, orderLinesReducer } from './orderLines'; - -export type ShopAction = OrderLineAction; - -export const shopReducer = combineReducers({ - orderLines: orderLinesReducer, -}); diff --git a/src/shop/reducers/orderLines.ts b/src/shop/reducers/orderLines.ts deleted file mode 100644 index 5645fc3d..00000000 --- a/src/shop/reducers/orderLines.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { batch } from 'react-redux'; -import { Reducer } from 'redux'; - -import { getUser } from 'authentication/api'; -import { IAuthUser } from 'authentication/models/User'; -import { Dispatch } from 'core/redux/Store'; -import { getOrders } from 'profile/api/orders'; -import { IOrderLine } from 'shop/models'; - -export type OrderLinesStatus = 'fetching' | 'error' | 'ready'; - -export interface IApiError { - error?: string; - message?: string; - non_field_errors?: string; - object?: { [Key in keyof T]?: string }; -} - -export interface IOrderLinesState { - orderLines: IOrderLine[]; - status: OrderLinesStatus; - errors?: IApiError; -} - -const INITIAL_STATE: IOrderLinesState = { - status: 'fetching', - orderLines: [], -}; - -export enum Type { - FETCH_ORDER_LINES = 'FETCH_ORDER_LINES', - SET_ORDER_LINES = 'SET_ORDER_LINES', - SET_STATUS = 'SET_STATUS_ORDER_LINES', - SET_ERRORS = 'SET_ORDER_LINES_ERRORS', -} - -export interface IFetchOrderLinesAction { - type: Type.FETCH_ORDER_LINES; -} - -export interface ISetOrderLinesAction { - type: Type.SET_ORDER_LINES; - orderLines: IOrderLine[]; -} - -export interface ISetStatusAction { - type: Type.SET_STATUS; - status: OrderLinesStatus; -} - -export interface ISetErrorsAction { - type: Type.SET_ERRORS; - errors?: IApiError; -} - -export type OrderLineAction = IFetchOrderLinesAction | ISetStatusAction | ISetOrderLinesAction | ISetErrorsAction; - -export const orderLinesReducer: Reducer = (state = INITIAL_STATE, action) => { - switch (action.type) { - case Type.SET_STATUS: - return { - ...state, - status: action.status, - }; - case Type.SET_ORDER_LINES: - return { - ...state, - orderLines: action.orderLines, - }; - case Type.SET_ERRORS: - return { - ...state, - errors: action.errors, - }; - case Type.FETCH_ORDER_LINES: - return { - ...state, - }; - default: - return state; - } -}; - -export const fetchOrderLines = () => async (dispatch: Dispatch) => { - dispatch({ type: Type.SET_STATUS, status: 'fetching' }); - try { - const user = await getUser(); - const orderLines = await getOrders(user as IAuthUser); - batch(() => { - dispatch({ type: Type.SET_ORDER_LINES, orderLines }); - dispatch({ type: Type.SET_STATUS, status: 'ready' }); - }); - } catch (errors) { - batch(() => { - dispatch({ type: Type.SET_STATUS, status: 'ready' }); - dispatch({ type: Type.SET_ERRORS, errors }); - }); - } -};