✨ UserDetail and UserList views
This commit is contained in:
1 parent
ec0549154d
commit
e2a84c25a6
16 files changed
+200
-21
No files matched your search
@@ -5,12 +5,16 @@ import { Deposit } from "./features/deposit/Deposit";
|
||||
import { Log } from "./features/log/Log";
|
||||
import { Register } from "./features/register/Register";
|
||||
import { Stats } from "./features/stats/Stats";
|
||||
import { UserDetail } from "./features/users/UserDetail";
|
||||
import { UserList } from "./features/users/UserList";
|
||||
|
||||
export const Routes: React.FC = () => (
|
||||
<ReactRouterRoutes>
|
||||
<Route path="/deposit" element={<Deposit />} />
|
||||
<Route path="/log" element={<Log />} />
|
||||
<Route path="/stats" element={<Stats />} />
|
||||
<Route path="/users" element={<UserList />} />
|
||||
<Route path="/user/:userId" element={<UserDetail />} />
|
||||
<Route path="/" element={<Register />} />
|
||||
</ReactRouterRoutes>
|
||||
);
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ReactComponent as BarChart } from "bootstrap-icons/icons/bar-chart.svg";
|
||||
import { ReactComponent as CardList } from "bootstrap-icons/icons/card-list.svg";
|
||||
import { ReactComponent as House } from "bootstrap-icons/icons/house.svg";
|
||||
import { ReactComponent as PeopleIcon } from "bootstrap-icons/icons/people.svg";
|
||||
import React from "react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
|
||||
@@ -64,6 +65,15 @@ export const Header: React.FC = () => {
|
||||
Statistikk
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
<li className="nav-item">
|
||||
<NavLink to="/users" className={navLinkStyles}>
|
||||
<span className={styles.navIcon}>
|
||||
<PeopleIcon />
|
||||
</span>{" "}
|
||||
Folk
|
||||
</NavLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import clsx from "clsx";
|
||||
import { parseISO, format } from "date-fns";
|
||||
import React from "react";
|
||||
import { useUrlSearchParams } from "use-url-search-params";
|
||||
|
||||
import { Order, OrderLine, OrderListDocument, Transaction, TransactionListDocument } from "../../types";
|
||||
import { formatTime } from "../../utils/time";
|
||||
import styles from "./Log.module.css";
|
||||
|
||||
const formatTime = (isoDateTime: string): string => format(parseISO(isoDateTime), "yyyy-MM-dd HH:mm");
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
|
||||
export const Log = () => {
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { parseISO, subDays } from "date-fns";
|
||||
import { useState } from "react";
|
||||
|
||||
import { User, Product, AllProductsDocument, AllUsersDocument } from "../../types";
|
||||
|
||||
const activeLimitDays = 365 / 2;
|
||||
import { activeUserFilter } from "../../utils/users";
|
||||
|
||||
type useRegisterType = {
|
||||
users: User[];
|
||||
@@ -20,13 +18,7 @@ export const useRegister = (): useRegisterType => {
|
||||
const { data: usersData, loading: usersLoading } = useQuery(AllUsersDocument);
|
||||
|
||||
const allUsers = usersData?.allUsers || [];
|
||||
const users = showAll
|
||||
? allUsers
|
||||
: allUsers.filter((user: User) => {
|
||||
const balance = user.profile?.balance || 0;
|
||||
const active = parseISO(user.profile?.lastPurchaseDate) >= subDays(new Date(), activeLimitDays);
|
||||
return balance >= 15 && active;
|
||||
});
|
||||
const users = showAll ? allUsers : allUsers.filter(activeUserFilter);
|
||||
|
||||
return { loading: loading || usersLoading, users, products: data?.allProducts || [], showAll, setShowAll };
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
.userDetail {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import React from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import { User, UserDetailDocument, UserProfile } from "../../types";
|
||||
import { formatMonth, formatTime } from "../../utils/time";
|
||||
import styles from "./UserDetail.module.css";
|
||||
|
||||
export const UserDetail = () => {
|
||||
const { userId } = useParams();
|
||||
const { data, loading } = useQuery(UserDetailDocument, { variables: { userId: userId || "" }, skip: !userId });
|
||||
|
||||
if (loading) return <div>Loading...</div>;
|
||||
|
||||
const user = data?.user as User;
|
||||
const profile = user?.profile as UserProfile;
|
||||
return (
|
||||
<div className={styles.userDetail}>
|
||||
<div>
|
||||
<h1>
|
||||
{profile.image && <img src={profile.image} alt={`${user.firstName}s profile picture`} />} {user.firstName}
|
||||
</h1>
|
||||
<div className="card">
|
||||
<div className="card-header">Stats</div>
|
||||
<ul className="list-group list-group-flush">
|
||||
<li className="list-group-item">Saldo: {profile.balance}</li>
|
||||
<li className="list-group-item">Siste kjøp: {formatTime(profile.lastPurchaseDate)}</li>
|
||||
<li className="list-group-item">Kjøp totalt: {profile.orderSumTotal.toLocaleString()}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<br />
|
||||
<div className="card">
|
||||
<div className="card-header">Top 5 måneder</div>
|
||||
<ul className="list-group list-group-flush">
|
||||
{profile.topMonths.map((topMonth) => (
|
||||
<li key={topMonth.period} className="list-group-item">
|
||||
{formatMonth(topMonth.period)}: <strong>{topMonth.count}</strong> kjøp på totalt{" "}
|
||||
<strong>{topMonth.sum} kr</strong>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div>TODO: produktgraf</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import React, { useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { AllUsersDocument } from "../../types";
|
||||
import { activeUserFilter } from "../../utils/users";
|
||||
|
||||
export const UserList = () => {
|
||||
const { data, loading } = useQuery(AllUsersDocument);
|
||||
const [showAll, setShowAll] = useState(false);
|
||||
|
||||
if (loading) return <div>Loading...</div>;
|
||||
|
||||
const allUsers = data?.allUsers || [];
|
||||
const users = showAll ? allUsers : allUsers.filter(activeUserFilter);
|
||||
return (
|
||||
<div>
|
||||
<button type="button" className="btn btn-link" onClick={() => setShowAll(!showAll)}>
|
||||
{showAll ? "Skjul gamliser" : "Vis alle"}
|
||||
</button>
|
||||
<ul>
|
||||
{(users || []).map((user) => (
|
||||
<li key={user.id}>
|
||||
<Link to={`/user/${user.id}`}>
|
||||
{user.firstName} {user.lastName}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
query userDetail($userId: ID!) {
|
||||
user(userId: $userId) {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
profile {
|
||||
id
|
||||
balance
|
||||
image
|
||||
lastPurchaseDate
|
||||
topMonths {
|
||||
period
|
||||
sum
|
||||
count
|
||||
}
|
||||
orderSumTotal
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,6 +159,7 @@ export type Query = {
|
||||
orderList: Array<Order>;
|
||||
orderStats: OrderStats;
|
||||
transactionList: Array<Transaction>;
|
||||
user: User;
|
||||
};
|
||||
|
||||
|
||||
@@ -178,6 +179,18 @@ export type QueryTransactionListArgs = {
|
||||
pagination?: InputMaybe<OffsetPaginationInput>;
|
||||
};
|
||||
|
||||
|
||||
export type QueryUserArgs = {
|
||||
userId: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type TopMonth = {
|
||||
__typename?: 'TopMonth';
|
||||
count: Scalars['Int'];
|
||||
period: Scalars['String'];
|
||||
sum: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type Transaction = {
|
||||
__typename?: 'Transaction';
|
||||
amount: Scalars['Float'];
|
||||
@@ -204,6 +217,8 @@ export type UserProfile = {
|
||||
id: Scalars['ID'];
|
||||
image: Scalars['String'];
|
||||
lastPurchaseDate: Scalars['DateTime'];
|
||||
orderSumTotal: Scalars['Float'];
|
||||
topMonths: Array<TopMonth>;
|
||||
};
|
||||
|
||||
export type CreateDepositMutationVariables = Exact<{
|
||||
@@ -263,6 +278,13 @@ export type ProductStatsQueryVariables = Exact<{
|
||||
|
||||
export type ProductStatsQuery = { __typename?: 'Query', allProducts: Array<{ __typename?: 'Product', id: string, name: string, userCounts: any }> };
|
||||
|
||||
export type UserDetailQueryVariables = Exact<{
|
||||
userId: Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type UserDetailQuery = { __typename?: 'Query', user: { __typename?: 'User', id: string, firstName: string, lastName: string, profile?: { __typename?: 'UserProfile', id: string, balance: number, image: string, lastPurchaseDate: any, orderSumTotal: number, topMonths: Array<{ __typename?: 'TopMonth', period: string, sum: number, count: number }> } | null } };
|
||||
|
||||
|
||||
export const CreateDepositDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateDeposit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"userId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"amount"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createDeposit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"userId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"userId"}}},{"kind":"Argument","name":{"kind":"Name","value":"amount"},"value":{"kind":"Variable","name":{"kind":"Name","value":"amount"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CreateDepositSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"transaction"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"balance"}}]}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AboveMaxSaldo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"maxSaldo"}},{"kind":"Field","name":{"kind":"Name","value":"above"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FormErrors"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"fields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"field"}},{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]}}]} as unknown as DocumentNode<CreateDepositMutation, CreateDepositMutationVariables>;
|
||||
export const OrderListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"orderList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"10"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"offset"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"orderList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"order"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"created"},"value":{"kind":"EnumValue","value":"DESC"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"offset"},"value":{"kind":"Variable","name":{"kind":"Name","value":"offset"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"isExternal"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"orderSum"}},{"kind":"Field","name":{"kind":"Name","value":"customer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"balance"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"orderlines"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"unitPrice"}},{"kind":"Field","name":{"kind":"Name","value":"product"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceExt"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceInt"}}]}}]}}]}}]}}]} as unknown as DocumentNode<OrderListQuery, OrderListQueryVariables>;
|
||||
@@ -272,3 +294,4 @@ export const AllUsersDocument = {"kind":"Document","definitions":[{"kind":"Opera
|
||||
export const CreateOrderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOrder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"customerId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orderLines"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"OrderLineInput"}}}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"isExternal"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOrder"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"customerId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"customerId"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderLines"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orderLines"}}},{"kind":"Argument","name":{"kind":"Name","value":"isExternal"},"value":{"kind":"Variable","name":{"kind":"Name","value":"isExternal"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CreateOrderSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"order"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"isExternal"}},{"kind":"Field","name":{"kind":"Name","value":"customer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"balance"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"orderlines"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"unitPrice"}},{"kind":"Field","name":{"kind":"Name","value":"product"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceExt"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceInt"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"orderSum"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"InsufficientFunds"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"amountLacking"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FormErrors"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"fields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"field"}},{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]}}]} as unknown as DocumentNode<CreateOrderMutation, CreateOrderMutationVariables>;
|
||||
export const OrderStatsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"orderStats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"orderStats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"yearly"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"period"}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}},{"kind":"Field","name":{"kind":"Name","value":"monthly"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"period"}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}},{"kind":"Field","name":{"kind":"Name","value":"daily"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"period"}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}},{"kind":"Field","name":{"kind":"Name","value":"hourly"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"period"}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}}]}}]}}]} as unknown as DocumentNode<OrderStatsQuery, OrderStatsQueryVariables>;
|
||||
export const ProductStatsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"productStats"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"active"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allProducts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"active"},"value":{"kind":"Variable","name":{"kind":"Name","value":"active"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"userCounts"}}]}}]}}]} as unknown as DocumentNode<ProductStatsQuery, ProductStatsQueryVariables>;
|
||||
export const UserDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"userDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"userId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"userId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"userId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"balance"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"lastPurchaseDate"}},{"kind":"Field","name":{"kind":"Name","value":"topMonths"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"period"}},{"kind":"Field","name":{"kind":"Name","value":"sum"}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}},{"kind":"Field","name":{"kind":"Name","value":"orderSumTotal"}}]}}]}}]}}]} as unknown as DocumentNode<UserDetailQuery, UserDetailQueryVariables>;
|
||||
@@ -0,0 +1,5 @@
|
||||
import { format, parseISO } from "date-fns";
|
||||
|
||||
export const formatTime = (isoDateTime: string): string => format(parseISO(isoDateTime), "yyyy-MM-dd HH:mm");
|
||||
|
||||
export const formatMonth = (isoDateTime: string): string => format(parseISO(isoDateTime), "yyyy-MM");
|
||||
@@ -0,0 +1,11 @@
|
||||
import { parseISO, subDays } from "date-fns";
|
||||
|
||||
import { User } from "../types";
|
||||
|
||||
const activeLimitDays = 365 / 2;
|
||||
|
||||
export const activeUserFilter = (user: User) => {
|
||||
const balance = user.profile?.balance || 0;
|
||||
const active = parseISO(user.profile?.lastPurchaseDate) >= subDays(new Date(), activeLimitDays);
|
||||
return balance >= 15 && active;
|
||||
};
|
||||
@@ -1,5 +1,7 @@
|
||||
import strawberry
|
||||
import strawberry_django
|
||||
from django.contrib.auth.models import User as DjangoUser
|
||||
from strawberry import ID
|
||||
|
||||
from main import models
|
||||
from main.api import mutations
|
||||
@@ -23,6 +25,10 @@ class Query:
|
||||
order_list: list[Order] = strawberry_django.field(pagination=True, order=OrderOrdering)
|
||||
transaction_list: list[Transaction] = strawberry_django.field(pagination=True, order=TransactionOrdering)
|
||||
|
||||
@strawberry.field
|
||||
def user(self, user_id: ID) -> User:
|
||||
return DjangoUser.objects.get(pk=user_id)
|
||||
|
||||
@strawberry.field
|
||||
def order_stats(self) -> OrderStats:
|
||||
return OrderStats(
|
||||
|
||||
+11
-2
@@ -36,15 +36,24 @@ class Product:
|
||||
user_counts: JSONScalar
|
||||
|
||||
|
||||
@strawberry.type
|
||||
class TopMonth:
|
||||
period: str
|
||||
count: int
|
||||
sum: int
|
||||
|
||||
|
||||
@strawberry_django.type(models.UserProfile)
|
||||
class UserProfile:
|
||||
id: auto
|
||||
balance: auto
|
||||
image: auto
|
||||
last_purchase_date: datetime
|
||||
order_sum_total: float
|
||||
|
||||
@strawberry.field()
|
||||
def last_purchase_date(self) -> datetime:
|
||||
return self.last_purchase_date()
|
||||
def top_months(self) -> list[TopMonth]:
|
||||
return [TopMonth(**month) for month in self.top_months()]
|
||||
|
||||
|
||||
@strawberry_django.type(UserModel)
|
||||
|
||||
+14
-2
@@ -5,7 +5,7 @@ from typing import Type
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.db.models import Count, Sum
|
||||
from django.db.models.functions import Extract, ExtractHour, ExtractIsoWeekDay, ExtractMonth, ExtractYear
|
||||
from django.db.models.functions import Extract, ExtractHour, ExtractIsoWeekDay, ExtractMonth, ExtractYear, TruncMonth
|
||||
|
||||
EXTERNAL_USER = "Ekstern"
|
||||
|
||||
@@ -178,11 +178,23 @@ class UserProfile(models.Model):
|
||||
def last_purchase(self):
|
||||
return self.user.orders.order_by("-created").first()
|
||||
|
||||
def last_purchase_date(self):
|
||||
def last_purchase_date(self) -> datetime:
|
||||
last = self.last_purchase()
|
||||
last_p = last.created if last else datetime.min
|
||||
return last_p
|
||||
|
||||
def top_months(self, limit=5):
|
||||
return (
|
||||
Order.objects.filter(customer_id=self.user_id)
|
||||
.annotate(period=TruncMonth("created"))
|
||||
.values("period")
|
||||
.annotate(sum=Sum("order_sum"), count=Count("pk"))
|
||||
.order_by("-sum")[:limit]
|
||||
)
|
||||
|
||||
def order_sum_total(self) -> float:
|
||||
return Order.objects.filter(customer_id=self.user_id).aggregate(sum=Sum("order_sum"))["sum"] or 0
|
||||
|
||||
def image_url(self) -> str:
|
||||
if self.user.email:
|
||||
md5_hash = hashlib.md5(self.user.email.lower().strip().encode()).hexdigest()
|
||||
|
||||
@@ -121,9 +121,16 @@ type Query {
|
||||
allOrders: [Order!]!
|
||||
orderList(pagination: OffsetPaginationInput, order: OrderOrdering): [Order!]!
|
||||
transactionList(pagination: OffsetPaginationInput, order: TransactionOrdering): [Transaction!]!
|
||||
user(userId: ID!): User!
|
||||
orderStats: OrderStats!
|
||||
}
|
||||
|
||||
type TopMonth {
|
||||
period: String!
|
||||
count: Int!
|
||||
sum: Int!
|
||||
}
|
||||
|
||||
type Transaction {
|
||||
id: ID!
|
||||
user: User!
|
||||
@@ -147,4 +154,6 @@ type UserProfile {
|
||||
balance: Float!
|
||||
image: String!
|
||||
lastPurchaseDate: DateTime!
|
||||
orderSumTotal: Float!
|
||||
topMonths: [TopMonth!]!
|
||||
}
|
||||
+1
-4
@@ -26,10 +26,7 @@
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="{% block active_register %}{% endblock %}"><a href="{% url "home" %}"><i class="glyphicon glyphicon-home white"></i> Kjøp drikke</a></li>
|
||||
<li class="{% block active_deposit %}{% endblock %}"><a href="{% url "deposit" %}">Sett inn penger</a></li>
|
||||
<li class="{% block active_log %}{% endblock %}"><a href="{% url "log" %}"><i class="glyphicon glyphicon-list-alt"></i> Logg</a></li>
|
||||
<li class="{% block active_stats %}{% endblock %}"><a href="{% url "stats" %}"><i class="glyphicon glyphicon-stats"></i> Statistikk</a></li>
|
||||
<li><a href="/"><i class="glyphicon glyphicon-home white"></i> Til fremtiden</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
{% if not user.is_authenticated %}
|
||||
|
||||
Reference in new issue
Block a user