diff --git a/frontend/src/features/stats/ProductStats.tsx b/frontend/src/features/stats/ProductStats.tsx
index e54fd7d..9bb7f6a 100644
--- a/frontend/src/features/stats/ProductStats.tsx
+++ b/frontend/src/features/stats/ProductStats.tsx
@@ -2,7 +2,7 @@ import { useQuery } from "@apollo/client";
import { Pie } from "@nivo/pie";
import React from "react";
-import { ProductStatsDocument } from "../../types";
+import { AllProductStatsDocument } from "../../types";
import styles from "./Stats.module.css";
import { StatsView } from "./StatsView";
@@ -12,7 +12,7 @@ type UserCount = {
};
export const ProductStats = () => {
- const { data, loading } = useQuery(ProductStatsDocument, {
+ const { data, loading } = useQuery(AllProductStatsDocument, {
variables: { active: true },
});
diff --git a/frontend/src/features/stats/Stats.tsx b/frontend/src/features/stats/Stats.tsx
index 8a6d7bb..371d75a 100644
--- a/frontend/src/features/stats/Stats.tsx
+++ b/frontend/src/features/stats/Stats.tsx
@@ -1,8 +1,70 @@
+import { useQuery } from "@apollo/client";
+import { Line } from "@nivo/line";
import React from "react";
+import { ProductStatsDocument } from "../../types";
import { StatsView } from "./StatsView";
-export const Stats = () => {
- const loading = false;
- return TODO;
+export const Stats = () => {
+ const { data, loading } = useQuery(ProductStatsDocument);
+
+ const series: any[] = [];
+ (data?.productStats || [])?.forEach(({ productName: id, data }) => {
+ const lineData: any[] = [];
+ data.forEach((point, index) => {
+ lineData.push({ x: point.x, y: point.y + (lineData?.[index - 1]?.y || 0) });
+ });
+ series.push({ id, data: lineData });
+ });
+
+ return (
+
+ Kjøp over tid
+ (
+
+
+ {point.data.xFormatted}
+
+
+ {" "}
+ {point.data.yFormatted} {point.serieId}
+
+
+ )}
+ />
+
+ );
};
diff --git a/frontend/src/features/stats/allProductUserCounts.graphql b/frontend/src/features/stats/allProductUserCounts.graphql
new file mode 100644
index 0000000..76ae94d
--- /dev/null
+++ b/frontend/src/features/stats/allProductUserCounts.graphql
@@ -0,0 +1,7 @@
+query allProductStats($active: Boolean) {
+ allProducts(filters: {active: $active}) {
+ id
+ name
+ userCounts
+ }
+}
diff --git a/frontend/src/features/stats/productStats.graphql b/frontend/src/features/stats/productStats.graphql
index 4275466..93e4d79 100644
--- a/frontend/src/features/stats/productStats.graphql
+++ b/frontend/src/features/stats/productStats.graphql
@@ -1,7 +1,9 @@
-query productStats($active: Boolean) {
- allProducts(filters: {active: $active}) {
- id
- name
- userCounts
- }
-}
+query productStats {
+ productStats {
+ productName
+ data {
+ x
+ y
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/features/users/UserDetail.tsx b/frontend/src/features/users/UserDetail.tsx
index 10eab00..fb08c63 100644
--- a/frontend/src/features/users/UserDetail.tsx
+++ b/frontend/src/features/users/UserDetail.tsx
@@ -58,6 +58,7 @@ export const UserDetail = () => {
+
Kjøp over tid
;
orderList: Array;
orderStats: OrderStats;
+ productStats: Array;
transactionList: Array;
user: User;
};
@@ -300,17 +301,22 @@ export type CreateOrderMutationVariables = Exact<{
export type CreateOrderMutation = { __typename?: 'Mutation', createOrder: { __typename?: 'CreateOrderSuccess', order: { __typename?: 'Order', id: string, isExternal: boolean, orderSum: number, customer?: { __typename?: 'User', id: string, firstName: string, lastName: string, profile?: { __typename?: 'UserProfile', id: string, balance: number } | null } | null, orderlines?: Array<{ __typename?: 'OrderLine', id: string, amount: number, unitPrice: number, product: { __typename?: 'Product', id: string, name: string, salePriceExt: number, salePriceInt: number } }> | null } } | { __typename?: 'FormErrors', error: boolean, message: string, fields: Array<{ __typename?: 'FieldError', field: string, message: string }> } | { __typename?: 'InsufficientFunds', error: boolean, message: string, amountLacking: number } };
+export type AllProductStatsQueryVariables = Exact<{
+ active?: InputMaybe;
+}>;
+
+
+export type AllProductStatsQuery = { __typename?: 'Query', allProducts: Array<{ __typename?: 'Product', id: string, name: string, userCounts: any }> };
+
export type OrderStatsQueryVariables = Exact<{ [key: string]: never; }>;
export type OrderStatsQuery = { __typename?: 'Query', orderStats: { __typename?: 'OrderStats', yearly: Array<{ __typename?: 'OrderStatsByTime', period: number, count: string }>, monthly: Array<{ __typename?: 'OrderStatsByTime', period: number, count: string }>, daily: Array<{ __typename?: 'OrderStatsByTime', period: number, count: string }>, hourly: Array<{ __typename?: 'OrderStatsByTime', period: number, count: string }> } };
-export type ProductStatsQueryVariables = Exact<{
- active?: InputMaybe;
-}>;
+export type ProductStatsQueryVariables = Exact<{ [key: string]: never; }>;
-export type ProductStatsQuery = { __typename?: 'Query', allProducts: Array<{ __typename?: 'Product', id: string, name: string, userCounts: any }> };
+export type ProductStatsQuery = { __typename?: 'Query', productStats: Array<{ __typename?: 'ProductOrderStat', productName: string, data: Array<{ __typename?: 'Point', x: string, y: number }> }> };
export type UserDetailQueryVariables = Exact<{
userId: Scalars['ID'];
@@ -327,6 +333,7 @@ export const TransactionListDocument = {"kind":"Document","definitions":[{"kind"
export const AllProductsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"allProducts"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"active"}},"type":{"kind":"NonNullType","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":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceExt"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceInt"}}]}}]}}]} as unknown as DocumentNode;
export const AllUsersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"allUsers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allUsers"},"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"}}]}}]}}]}}]} as unknown as DocumentNode;
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;
+export const AllProductStatsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"allProductStats"},"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;
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;
-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;
+export const ProductStatsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"productStats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"productStats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"productName"}},{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"x"}},{"kind":"Field","name":{"kind":"Name","value":"y"}}]}}]}}]}}]} as unknown as DocumentNode;
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":"productOrderStats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"productName"}},{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"x"}},{"kind":"Field","name":{"kind":"Name","value":"y"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"orderSumTotal"}}]}}]}}]}}]} as unknown as DocumentNode;
\ No newline at end of file
diff --git a/main/api/queries/shop.py b/main/api/queries/shop.py
index 1fe860c..97ec9c6 100644
--- a/main/api/queries/shop.py
+++ b/main/api/queries/shop.py
@@ -2,7 +2,7 @@ import strawberry
import strawberry_django
from main import models
-from main.api.types.shop import Order, OrderOrdering, OrderStats, OrderStatsByTime, Product
+from main.api.types.shop import Order, OrderOrdering, OrderStats, OrderStatsByTime, Point, Product, ProductOrderStat
@strawberry.type
@@ -22,3 +22,10 @@ class ShopQueries:
for period in ["yearly", "monthly", "daily", "hourly"]
}
)
+
+ @strawberry.field
+ def product_stats(self) -> list[ProductOrderStat]:
+ return [
+ ProductOrderStat(product_name=stat["product_name"], data=[Point(**p) for p in stat["data"]])
+ for stat in models.OrderLine.objects.product_order_stats().as_series()
+ ]
diff --git a/main/api/types/shop.py b/main/api/types/shop.py
index 780f238..cd01406 100644
--- a/main/api/types/shop.py
+++ b/main/api/types/shop.py
@@ -3,7 +3,6 @@ import strawberry_django
from strawberry_django import auto
from main import models
-from main.api.types.users import User
from main.api.types.utils import JSONScalar
@@ -32,7 +31,7 @@ class OrderOrdering:
@strawberry_django.type(models.Order)
class Order:
id: auto
- customer: User
+ customer: strawberry.LazyType["User", "main.api.types.users"]
order_sum: auto
created: auto
orderlines: list["OrderLine"]
@@ -60,3 +59,15 @@ class OrderStats:
monthly: list[OrderStatsByTime]
daily: list[OrderStatsByTime]
hourly: list[OrderStatsByTime]
+
+
+@strawberry.type
+class Point:
+ x: str
+ y: int
+
+
+@strawberry.type
+class ProductOrderStat:
+ product_name: str
+ data: list[Point]
diff --git a/main/api/types/users.py b/main/api/types/users.py
index 995e4b9..5937b8c 100644
--- a/main/api/types/users.py
+++ b/main/api/types/users.py
@@ -6,6 +6,7 @@ from django.contrib.auth import get_user_model
from strawberry_django import auto
from main import models
+from main.api.types.shop import Point, ProductOrderStat
UserModel = get_user_model()
@@ -17,18 +18,6 @@ class TopMonth:
sum: int
-@strawberry.type
-class Point:
- x: str
- y: int
-
-
-@strawberry.type
-class ProductOrderStat:
- product_name: str
- data: list[Point]
-
-
@strawberry_django.type(models.UserProfile)
class UserProfile:
id: auto
diff --git a/schema.graphql b/schema.graphql
index eee6dd4..10e1672 100644
--- a/schema.graphql
+++ b/schema.graphql
@@ -132,6 +132,7 @@ type Query {
allOrders: [Order!]!
orderList(pagination: OffsetPaginationInput, order: OrderOrdering): [Order!]!
orderStats: OrderStats!
+ productStats: [ProductOrderStat!]!
allUsers: [User!]!
user(userId: ID!): User!
}