10 Commits
Author SHA1 Message Date
angel 9f86b8e95c bad logs
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 20s
2026-08-20 00:23:41 -06:00
angel fa3db63ad2 protoype: list raw events on mock mode reports page 2026-08-20 00:23:41 -06:00
angel 0e0422545b mock events saved to db 2026-08-20 00:23:41 -06:00
angel e4e2bd992b mock events db schema 2026-08-20 00:23:41 -06:00
angel 20bbbe33ca regenerated diagrams 2026-08-20 00:23:41 -06:00
angel 496310c93a fixed mock mode button; shortened simulation tabs 2026-08-20 00:23:41 -06:00
angel 25615fabc6 MockMode: made global in templates; increase bort of navbar in mock mode 2026-08-20 00:23:41 -06:00
angel f30d4a3140 stubbed simulations page content: set inventory 2026-08-20 00:23:41 -06:00
angel 793ca05fd4 stubbed simulations page content: refund 2026-08-20 00:23:41 -06:00
angel 9971ff3266 stubbed simulations page content: sale 2026-08-20 00:23:41 -06:00
27 changed files with 1933 additions and 97 deletions
@@ -0,0 +1,63 @@
BEGIN;
--- event processing
DROP TRIGGER amazon_store_events ON mock.raw_shop_events;
DROP FUNCTION mock.process_raw_amazon_event;
DROP TRIGGER big_cartel_store_events ON mock.raw_shop_events;
DROP FUNCTION mock.process_raw_big_cartel_event;
DROP TRIGGER ebay_store_events ON mock.raw_shop_events;
DROP FUNCTION mock.process_raw_ebay_event;
DROP TRIGGER ecwid_store_events ON mock.raw_shop_events;
DROP FUNCTION mock.process_raw_ecwid_event;
DROP TRIGGER etsy_store_events ON mock.raw_shop_events;
DROP FUNCTION mock.process_raw_etsy_event;
DROP TRIGGER shopify_store_events ON mock.raw_shop_events;
DROP FUNCTION mock.process_raw_shopify_event;
DROP TRIGGER square_online_store_events ON mock.raw_shop_events;
DROP FUNCTION mock.process_raw_square_online_event;
DROP TRIGGER squarespace_store_events ON mock.raw_shop_events;
DROP FUNCTION mock.process_raw_squarespace_event;
DROP TRIGGER tiktok_store_events ON mock.raw_shop_events;
DROP FUNCTION mock.process_raw_tiktok_event;
DROP TRIGGER walmart_marketplace_store_events ON mock.raw_shop_events;
DROP FUNCTION mock.process_raw_walmart_marketplace_event;
DROP TRIGGER wix_store_events ON mock.raw_shop_events;
DROP FUNCTION mock.process_raw_wix_event;
DROP TRIGGER woo_commerce_store_events ON mock.raw_shop_events;
DROP FUNCTION mock.process_raw_woo_commerce_event;
DROP TRIGGER zoho_store_events ON mock.raw_shop_events;
DROP FUNCTION mock.process_raw_zoho_event;
-- event tables
DROP TABLE mock.shop_amazon_events;
DROP TABLE mock.shop_big_cartel_events;
DROP TABLE mock.shop_ebay_events;
DROP TABLE mock.shop_ecwid_events;
DROP TABLE mock.shop_etsy_events;
DROP TABLE mock.shop_shopify_events;
DROP TABLE mock.shop_square_online_events;
DROP TABLE mock.shop_squarespace_events;
DROP TABLE mock.shop_tiktok_events;
DROP TABLE mock.shop_walmart_marketplace_events;
DROP TABLE mock.shop_wix_events;
DROP TABLE mock.shop_woo_commerce_events;
DROP TABLE mock.shop_zoho_events;
DROP TABLE mock.raw_shop_events;
COMMIT;
@@ -0,0 +1,464 @@
BEGIN;
-- raw shop events
CREATE TABLE mock.raw_shop_events (
platform TEXT NOT NULL,
shop_id TEXT NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL,
event_id TEXT NOT NULL,
raw_payload JSONB NOT NULL,
PRIMARY KEY (platform, shop_id, event_id, event_timestamp)
);
-- processed event tables
CREATE TABLE mock.shop_amazon_events (
platform TEXT NOT NULL DEFAULT 'amazon' CHECK (platform = 'amazon'),
shop_id TEXT NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL,
event_id TEXT NOT NULL,
PRIMARY KEY (shop_id, event_id, event_timestamp),
FOREIGN KEY (platform, shop_id, event_id, event_timestamp) REFERENCES mock.raw_shop_events
);
CREATE TABLE mock.shop_big_cartel_events (
platform TEXT NOT NULL DEFAULT 'big_cartel' CHECK (platform = 'big_cartel'),
shop_id TEXT NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL,
event_id TEXT NOT NULL,
PRIMARY KEY (shop_id, event_id, event_timestamp),
FOREIGN KEY (platform, shop_id, event_id, event_timestamp) REFERENCES mock.raw_shop_events
);
CREATE TABLE mock.shop_ebay_events (
platform TEXT NOT NULL DEFAULT 'ebay' CHECK (platform = 'ebay'),
shop_id TEXT NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL,
event_id TEXT NOT NULL,
PRIMARY KEY (shop_id, event_id, event_timestamp),
FOREIGN KEY (platform, shop_id, event_id, event_timestamp) REFERENCES mock.raw_shop_events
);
CREATE TABLE mock.shop_ecwid_events (
platform TEXT NOT NULL DEFAULT 'ecwid' CHECK (platform = 'ecwid'),
shop_id TEXT NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL,
event_id TEXT NOT NULL,
PRIMARY KEY (shop_id, event_id, event_timestamp),
FOREIGN KEY (platform, shop_id, event_id, event_timestamp) REFERENCES mock.raw_shop_events
);
CREATE TABLE mock.shop_etsy_events (
platform TEXT NOT NULL DEFAULT 'etsy' CHECK (platform = 'etsy'),
shop_id TEXT NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL,
event_id TEXT NOT NULL,
PRIMARY KEY (shop_id, event_id, event_timestamp),
FOREIGN KEY (platform, shop_id, event_id, event_timestamp) REFERENCES mock.raw_shop_events
);
CREATE TABLE mock.shop_shopify_events (
platform TEXT NOT NULL DEFAULT 'shopify' CHECK (platform = 'shopify'),
shop_id TEXT NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL,
event_id TEXT NOT NULL,
PRIMARY KEY (shop_id, event_id, event_timestamp),
FOREIGN KEY (platform, shop_id, event_id, event_timestamp) REFERENCES mock.raw_shop_events
);
CREATE TABLE mock.shop_square_online_events (
platform TEXT NOT NULL DEFAULT 'square_online' CHECK (platform = 'square_online'),
shop_id TEXT NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL,
event_id TEXT NOT NULL,
PRIMARY KEY (shop_id, event_id, event_timestamp),
FOREIGN KEY (platform, shop_id, event_id, event_timestamp) REFERENCES mock.raw_shop_events
);
CREATE TABLE mock.shop_squarespace_events (
platform TEXT NOT NULL DEFAULT 'squarespace' CHECK (platform = 'squarespace'),
shop_id TEXT NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL,
event_id TEXT NOT NULL,
PRIMARY KEY (shop_id, event_id, event_timestamp),
FOREIGN KEY (platform, shop_id, event_id, event_timestamp) REFERENCES mock.raw_shop_events
);
CREATE TABLE mock.shop_tiktok_events (
platform TEXT NOT NULL DEFAULT 'tiktok' CHECK (platform = 'tiktok'),
shop_id TEXT NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL,
event_id TEXT NOT NULL,
PRIMARY KEY (shop_id, event_id, event_timestamp),
FOREIGN KEY (platform, shop_id, event_id, event_timestamp) REFERENCES mock.raw_shop_events
);
CREATE TABLE mock.shop_walmart_marketplace_events (
platform TEXT NOT NULL DEFAULT 'walmart_marketplace' CHECK (platform = 'walmart_marketplace'),
shop_id TEXT NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL,
event_id TEXT NOT NULL,
PRIMARY KEY (shop_id, event_id, event_timestamp),
FOREIGN KEY (platform, shop_id, event_id, event_timestamp) REFERENCES mock.raw_shop_events
);
CREATE TABLE mock.shop_wix_events (
platform TEXT NOT NULL DEFAULT 'wix' CHECK (platform = 'wix'),
shop_id TEXT NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL,
event_id TEXT NOT NULL,
PRIMARY KEY (shop_id, event_id, event_timestamp),
FOREIGN KEY (platform, shop_id, event_id, event_timestamp) REFERENCES mock.raw_shop_events
);
CREATE TABLE mock.shop_woo_commerce_events (
platform TEXT NOT NULL DEFAULT 'woo_commerce' CHECK (platform = 'woo_commerce'),
shop_id TEXT NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL,
event_id TEXT NOT NULL,
PRIMARY KEY (shop_id, event_id, event_timestamp),
FOREIGN KEY (platform, shop_id, event_id, event_timestamp) REFERENCES mock.raw_shop_events
);
CREATE TABLE mock.shop_zoho_events (
platform TEXT NOT NULL DEFAULT 'zoho' CHECK (platform = 'zoho'),
shop_id TEXT NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL,
event_id TEXT NOT NULL,
PRIMARY KEY (shop_id, event_id, event_timestamp),
FOREIGN KEY (platform, shop_id, event_id, event_timestamp) REFERENCES mock.raw_shop_events
);
--- event processing
CREATE OR REPLACE FUNCTION mock.process_raw_amazon_event() RETURNS TRIGGER AS $process_raw_amazon_event$
BEGIN
INSERT INTO mock.shop_amazon_events (
platform,
shop_id,
event_timestamp,
event_id
)
SELECT
NEW.platform,
NEW.shop_id,
NEW.event_timestamp,
NEW.event_id;
RETURN NULL;
END;
$process_raw_amazon_event$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER amazon_store_events
AFTER INSERT ON mock.raw_shop_events
FOR EACH ROW
WHEN (NEW.platform = 'amazon')
EXECUTE FUNCTION mock.process_raw_amazon_event();
CREATE OR REPLACE FUNCTION mock.process_raw_big_cartel_event() RETURNS TRIGGER AS $process_raw_big_cartel_event$
BEGIN
INSERT INTO mock.shop_big_cartel_events (
platform,
shop_id,
event_timestamp,
event_id
)
SELECT
NEW.platform,
NEW.shop_id,
NEW.event_timestamp,
NEW.event_id;
RETURN NULL;
END;
$process_raw_big_cartel_event$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER big_cartel_store_events
AFTER INSERT ON mock.raw_shop_events
FOR EACH ROW
WHEN (NEW.platform = 'big_cartel')
EXECUTE FUNCTION mock.process_raw_big_cartel_event();
CREATE OR REPLACE FUNCTION mock.process_raw_ebay_event() RETURNS TRIGGER AS $process_raw_ebay_event$
BEGIN
INSERT INTO mock.shop_ebay_events (
platform,
shop_id,
event_timestamp,
event_id
)
SELECT
NEW.platform,
NEW.shop_id,
NEW.event_timestamp,
NEW.event_id;
RETURN NULL;
END;
$process_raw_ebay_event$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER ebay_store_events
AFTER INSERT ON mock.raw_shop_events
FOR EACH ROW
WHEN (NEW.platform = 'ebay')
EXECUTE FUNCTION mock.process_raw_ebay_event();
CREATE OR REPLACE FUNCTION mock.process_raw_ecwid_event() RETURNS TRIGGER AS $process_raw_ecwid_event$
BEGIN
INSERT INTO mock.shop_ecwid_events (
platform,
shop_id,
event_timestamp,
event_id
)
SELECT
NEW.platform,
NEW.shop_id,
NEW.event_timestamp,
NEW.event_id;
RETURN NULL;
END;
$process_raw_ecwid_event$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER ecwid_store_events
AFTER INSERT ON mock.raw_shop_events
FOR EACH ROW
WHEN (NEW.platform = 'ecwid')
EXECUTE FUNCTION mock.process_raw_ecwid_event();
CREATE OR REPLACE FUNCTION mock.process_raw_etsy_event() RETURNS TRIGGER AS $process_raw_etsy_event$
BEGIN
INSERT INTO mock.shop_etsy_events (
platform,
shop_id,
event_timestamp,
event_id
)
SELECT
NEW.platform,
NEW.shop_id,
NEW.event_timestamp,
NEW.event_id;
RETURN NULL;
END;
$process_raw_etsy_event$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER etsy_store_events
AFTER INSERT ON mock.raw_shop_events
FOR EACH ROW
WHEN (NEW.platform = 'etsy')
EXECUTE FUNCTION mock.process_raw_etsy_event();
CREATE OR REPLACE FUNCTION mock.process_raw_shopify_event() RETURNS TRIGGER AS $process_raw_shopify_event$
BEGIN
INSERT INTO mock.shop_shopify_events (
platform,
shop_id,
event_timestamp,
event_id
)
SELECT
NEW.platform,
NEW.shop_id,
NEW.event_timestamp,
NEW.event_id;
RETURN NULL;
END;
$process_raw_shopify_event$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER shopify_store_events
AFTER INSERT ON mock.raw_shop_events
FOR EACH ROW
WHEN (NEW.platform = 'shopify')
EXECUTE FUNCTION mock.process_raw_shopify_event();
CREATE OR REPLACE FUNCTION mock.process_raw_square_online_event() RETURNS TRIGGER AS $process_raw_square_online_event$
BEGIN
INSERT INTO mock.shop_square_online_events (
platform,
shop_id,
event_timestamp,
event_id
)
SELECT
NEW.platform,
NEW.shop_id,
NEW.event_timestamp,
NEW.event_id;
RETURN NULL;
END;
$process_raw_square_online_event$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER square_online_store_events
AFTER INSERT ON mock.raw_shop_events
FOR EACH ROW
WHEN (NEW.platform = 'square_online')
EXECUTE FUNCTION mock.process_raw_square_online_event();
CREATE OR REPLACE FUNCTION mock.process_raw_squarespace_event() RETURNS TRIGGER AS $process_raw_squarespace_event$
BEGIN
INSERT INTO mock.shop_squarespace_events (
platform,
shop_id,
event_timestamp,
event_id
)
SELECT
NEW.platform,
NEW.shop_id,
NEW.event_timestamp,
NEW.event_id;
RETURN NULL;
END;
$process_raw_squarespace_event$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER squarespace_store_events
AFTER INSERT ON mock.raw_shop_events
FOR EACH ROW
WHEN (NEW.platform = 'squarespace')
EXECUTE FUNCTION mock.process_raw_squarespace_event();
CREATE OR REPLACE FUNCTION mock.process_raw_tiktok_event() RETURNS TRIGGER AS $process_raw_tiktok_event$
BEGIN
INSERT INTO mock.shop_tiktok_events (
platform,
shop_id,
event_timestamp,
event_id
)
SELECT
NEW.platform,
NEW.shop_id,
NEW.event_timestamp,
NEW.event_id;
RETURN NULL;
END;
$process_raw_tiktok_event$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER tiktok_store_events
AFTER INSERT ON mock.raw_shop_events
FOR EACH ROW
WHEN (NEW.platform = 'tiktok')
EXECUTE FUNCTION mock.process_raw_tiktok_event();
CREATE OR REPLACE FUNCTION mock.process_raw_walmart_marketplace_event() RETURNS TRIGGER AS $process_raw_walmart_marketplace_event$
BEGIN
INSERT INTO mock.shop_walmart_marketplace_events (
platform,
shop_id,
event_timestamp,
event_id
)
SELECT
NEW.platform,
NEW.shop_id,
NEW.event_timestamp,
NEW.event_id;
RETURN NULL;
END;
$process_raw_walmart_marketplace_event$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER walmart_marketplace_store_events
AFTER INSERT ON mock.raw_shop_events
FOR EACH ROW
WHEN (NEW.platform = 'walmart_marketplace')
EXECUTE FUNCTION mock.process_raw_walmart_marketplace_event();
CREATE OR REPLACE FUNCTION mock.process_raw_wix_event() RETURNS TRIGGER AS $process_raw_wix_event$
BEGIN
INSERT INTO mock.shop_wix_events (
platform,
shop_id,
event_timestamp,
event_id
)
SELECT
NEW.platform,
NEW.shop_id,
NEW.event_timestamp,
NEW.event_id;
RETURN NULL;
END;
$process_raw_wix_event$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER wix_store_events
AFTER INSERT ON mock.raw_shop_events
FOR EACH ROW
WHEN (NEW.platform = 'wix')
EXECUTE FUNCTION mock.process_raw_wix_event();
CREATE OR REPLACE FUNCTION mock.process_raw_woo_commerce_event() RETURNS TRIGGER AS $process_raw_woo_commerce_event$
BEGIN
INSERT INTO mock.shop_woo_commerce_events (
platform,
shop_id,
event_timestamp,
event_id
)
SELECT
NEW.platform,
NEW.shop_id,
NEW.event_timestamp,
NEW.event_id;
RETURN NULL;
END;
$process_raw_woo_commerce_event$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER woo_commerce_store_events
AFTER INSERT ON mock.raw_shop_events
FOR EACH ROW
WHEN (NEW.platform = 'woo_commerce')
EXECUTE FUNCTION mock.process_raw_woo_commerce_event();
CREATE OR REPLACE FUNCTION mock.process_raw_zoho_event() RETURNS TRIGGER AS $process_raw_zoho_event$
BEGIN
INSERT INTO mock.shop_zoho_events (
platform,
shop_id,
event_timestamp,
event_id
)
SELECT
NEW.platform,
NEW.shop_id,
NEW.event_timestamp,
NEW.event_id;
RETURN NULL;
END;
$process_raw_zoho_event$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER zoho_store_events
AFTER INSERT ON mock.raw_shop_events
FOR EACH ROW
WHEN (NEW.platform = 'zoho')
EXECUTE FUNCTION mock.process_raw_zoho_event();
COMMIT;
@@ -0,0 +1,9 @@
BEGIN;
DROP TABLE mock.sale_form_shop_selection;
DROP TABLE mock.refund_form_shop_selection;
DROP TABLE mock.count_form_shop_selection;
COMMIT;
@@ -0,0 +1,23 @@
BEGIN;
CREATE TABLE mock.sale_form_shop_selection (
account_id INTEGER PRIMARY KEY,
platform PLATFORM NOT NULL,
shop_id TEXT NOT NULL
);
CREATE TABLE mock.refund_form_shop_selection (
account_id INTEGER PRIMARY KEY,
platform PLATFORM NOT NULL,
shop_id TEXT NOT NULL
);
CREATE TABLE mock.count_form_shop_selection (
account_id INTEGER PRIMARY KEY,
platform PLATFORM NOT NULL,
shop_id TEXT NOT NULL
);
COMMIT;
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 414 KiB

After

Width:  |  Height:  |  Size: 471 KiB

+140
View File
@@ -9,6 +9,16 @@ entity "**accounts**" {
*""user_id"": //text [FK]// *""user_id"": //text [FK]//
} }
entity "**raw_shop_events**" {
+ ""platform"": //text [PK]//
+ ""store_id"": //text [PK]//
+ ""event_timestamp"": //timestamp with time zone [PK]//
+ ""event_id"": //text [PK]//
--
*""raw_payload"": //jsonb //
*""parsed"": //boolean //
}
entity "**shop_amazon**" { entity "**shop_amazon**" {
+ ""account_id"": //integer [PK][FK]// + ""account_id"": //integer [PK][FK]//
+ ""shop_id"": //text [PK]// + ""shop_id"": //text [PK]//
@@ -17,6 +27,14 @@ entity "**shop_amazon**" {
*""name"": //text // *""name"": //text //
} }
entity "**shop_amazon_events**" {
+ ""store_id"": //text [PK]//
+ ""event_timestamp"": //timestamp with time zone [PK][FK]//
+ ""event_id"": //text [PK]//
--
*""platform"": //text //
}
entity "**shop_amazon_listings**" { entity "**shop_amazon_listings**" {
+ ""shop_id"": //text [PK][FK]// + ""shop_id"": //text [PK][FK]//
+ ""listing_id"": //text [PK]// + ""listing_id"": //text [PK]//
@@ -63,6 +81,14 @@ entity "**shop_big_cartel**" {
*""name"": //text // *""name"": //text //
} }
entity "**shop_big_cartel_events**" {
+ ""store_id"": //text [PK]//
+ ""event_timestamp"": //timestamp with time zone [PK][FK]//
+ ""event_id"": //text [PK]//
--
*""platform"": //text //
}
entity "**shop_big_cartel_listings**" { entity "**shop_big_cartel_listings**" {
+ ""shop_id"": //text [PK][FK]// + ""shop_id"": //text [PK][FK]//
+ ""listing_id"": //text [PK]// + ""listing_id"": //text [PK]//
@@ -109,6 +135,14 @@ entity "**shop_ebay**" {
*""name"": //text // *""name"": //text //
} }
entity "**shop_ebay_events**" {
+ ""store_id"": //text [PK]//
+ ""event_timestamp"": //timestamp with time zone [PK][FK]//
+ ""event_id"": //text [PK]//
--
*""platform"": //text //
}
entity "**shop_ebay_listings**" { entity "**shop_ebay_listings**" {
+ ""shop_id"": //text [PK][FK]// + ""shop_id"": //text [PK][FK]//
+ ""listing_id"": //text [PK]// + ""listing_id"": //text [PK]//
@@ -155,6 +189,14 @@ entity "**shop_ecwid**" {
*""name"": //text // *""name"": //text //
} }
entity "**shop_ecwid_events**" {
+ ""store_id"": //text [PK]//
+ ""event_timestamp"": //timestamp with time zone [PK][FK]//
+ ""event_id"": //text [PK]//
--
*""platform"": //text //
}
entity "**shop_ecwid_listings**" { entity "**shop_ecwid_listings**" {
+ ""shop_id"": //text [PK][FK]// + ""shop_id"": //text [PK][FK]//
+ ""listing_id"": //text [PK]// + ""listing_id"": //text [PK]//
@@ -201,6 +243,14 @@ entity "**shop_etsy**" {
*""name"": //text // *""name"": //text //
} }
entity "**shop_etsy_events**" {
+ ""store_id"": //text [PK]//
+ ""event_timestamp"": //timestamp with time zone [PK][FK]//
+ ""event_id"": //text [PK]//
--
*""platform"": //text //
}
entity "**shop_etsy_listings**" { entity "**shop_etsy_listings**" {
+ ""shop_id"": //text [PK][FK]// + ""shop_id"": //text [PK][FK]//
+ ""listing_id"": //text [PK]// + ""listing_id"": //text [PK]//
@@ -247,6 +297,14 @@ entity "**shop_shopify**" {
*""name"": //text // *""name"": //text //
} }
entity "**shop_shopify_events**" {
+ ""store_id"": //text [PK]//
+ ""event_timestamp"": //timestamp with time zone [PK][FK]//
+ ""event_id"": //text [PK]//
--
*""platform"": //text //
}
entity "**shop_shopify_listings**" { entity "**shop_shopify_listings**" {
+ ""shop_id"": //text [PK][FK]// + ""shop_id"": //text [PK][FK]//
+ ""listing_id"": //text [PK]// + ""listing_id"": //text [PK]//
@@ -293,6 +351,14 @@ entity "**shop_square_online**" {
*""name"": //text // *""name"": //text //
} }
entity "**shop_square_online_events**" {
+ ""store_id"": //text [PK]//
+ ""event_timestamp"": //timestamp with time zone [PK][FK]//
+ ""event_id"": //text [PK]//
--
*""platform"": //text //
}
entity "**shop_square_online_listings**" { entity "**shop_square_online_listings**" {
+ ""shop_id"": //text [PK][FK]// + ""shop_id"": //text [PK][FK]//
+ ""listing_id"": //text [PK]// + ""listing_id"": //text [PK]//
@@ -339,6 +405,14 @@ entity "**shop_squarespace**" {
*""name"": //text // *""name"": //text //
} }
entity "**shop_squarespace_events**" {
+ ""store_id"": //text [PK]//
+ ""event_timestamp"": //timestamp with time zone [PK][FK]//
+ ""event_id"": //text [PK]//
--
*""platform"": //text //
}
entity "**shop_squarespace_listings**" { entity "**shop_squarespace_listings**" {
+ ""shop_id"": //text [PK][FK]// + ""shop_id"": //text [PK][FK]//
+ ""listing_id"": //text [PK]// + ""listing_id"": //text [PK]//
@@ -385,6 +459,14 @@ entity "**shop_tiktok**" {
*""name"": //text // *""name"": //text //
} }
entity "**shop_tiktok_events**" {
+ ""store_id"": //text [PK]//
+ ""event_timestamp"": //timestamp with time zone [PK][FK]//
+ ""event_id"": //text [PK]//
--
*""platform"": //text //
}
entity "**shop_tiktok_listings**" { entity "**shop_tiktok_listings**" {
+ ""shop_id"": //text [PK][FK]// + ""shop_id"": //text [PK][FK]//
+ ""listing_id"": //text [PK]// + ""listing_id"": //text [PK]//
@@ -431,6 +513,14 @@ entity "**shop_walmart_marketplace**" {
*""name"": //text // *""name"": //text //
} }
entity "**shop_walmart_marketplace_events**" {
+ ""store_id"": //text [PK]//
+ ""event_timestamp"": //timestamp with time zone [PK][FK]//
+ ""event_id"": //text [PK]//
--
*""platform"": //text //
}
entity "**shop_walmart_marketplace_listings**" { entity "**shop_walmart_marketplace_listings**" {
+ ""shop_id"": //text [PK][FK]// + ""shop_id"": //text [PK][FK]//
+ ""listing_id"": //text [PK]// + ""listing_id"": //text [PK]//
@@ -477,6 +567,14 @@ entity "**shop_wix**" {
*""name"": //text // *""name"": //text //
} }
entity "**shop_wix_events**" {
+ ""store_id"": //text [PK]//
+ ""event_timestamp"": //timestamp with time zone [PK][FK]//
+ ""event_id"": //text [PK]//
--
*""platform"": //text //
}
entity "**shop_wix_listings**" { entity "**shop_wix_listings**" {
+ ""shop_id"": //text [PK][FK]// + ""shop_id"": //text [PK][FK]//
+ ""listing_id"": //text [PK]// + ""listing_id"": //text [PK]//
@@ -523,6 +621,14 @@ entity "**shop_woo_commerce**" {
*""name"": //text // *""name"": //text //
} }
entity "**shop_woo_commerce_events**" {
+ ""store_id"": //text [PK]//
+ ""event_timestamp"": //timestamp with time zone [PK][FK]//
+ ""event_id"": //text [PK]//
--
*""platform"": //text //
}
entity "**shop_woo_commerce_listings**" { entity "**shop_woo_commerce_listings**" {
+ ""shop_id"": //text [PK][FK]// + ""shop_id"": //text [PK][FK]//
+ ""listing_id"": //text [PK]// + ""listing_id"": //text [PK]//
@@ -569,6 +675,14 @@ entity "**shop_zoho**" {
*""name"": //text // *""name"": //text //
} }
entity "**shop_zoho_events**" {
+ ""store_id"": //text [PK]//
+ ""event_timestamp"": //timestamp with time zone [PK][FK]//
+ ""event_id"": //text [PK]//
--
*""platform"": //text //
}
entity "**shop_zoho_listings**" { entity "**shop_zoho_listings**" {
+ ""shop_id"": //text [PK][FK]// + ""shop_id"": //text [PK][FK]//
+ ""listing_id"": //text [PK]// + ""listing_id"": //text [PK]//
@@ -654,6 +768,8 @@ entity "**sync_groups**" {
"**shop_amazon**" }-- "**public.oauth_users**" "**shop_amazon**" }-- "**public.oauth_users**"
"**shop_amazon_events**" ||-|| "**raw_shop_events**"
"**shop_amazon_listings**" }-- "**accounts**" "**shop_amazon_listings**" }-- "**accounts**"
"**shop_amazon_listings**" ||-|| "**shop_amazon**" "**shop_amazon_listings**" ||-|| "**shop_amazon**"
@@ -684,6 +800,8 @@ entity "**sync_groups**" {
"**shop_big_cartel**" }-- "**public.oauth_users**" "**shop_big_cartel**" }-- "**public.oauth_users**"
"**shop_big_cartel_events**" ||-|| "**raw_shop_events**"
"**shop_big_cartel_listings**" }-- "**accounts**" "**shop_big_cartel_listings**" }-- "**accounts**"
"**shop_big_cartel_listings**" ||-|| "**shop_big_cartel**" "**shop_big_cartel_listings**" ||-|| "**shop_big_cartel**"
@@ -714,6 +832,8 @@ entity "**sync_groups**" {
"**shop_ebay**" }-- "**public.oauth_users**" "**shop_ebay**" }-- "**public.oauth_users**"
"**shop_ebay_events**" ||-|| "**raw_shop_events**"
"**shop_ebay_listings**" }-- "**accounts**" "**shop_ebay_listings**" }-- "**accounts**"
"**shop_ebay_listings**" ||-|| "**shop_ebay**" "**shop_ebay_listings**" ||-|| "**shop_ebay**"
@@ -744,6 +864,8 @@ entity "**sync_groups**" {
"**shop_ecwid**" }-- "**public.oauth_users**" "**shop_ecwid**" }-- "**public.oauth_users**"
"**shop_ecwid_events**" ||-|| "**raw_shop_events**"
"**shop_ecwid_listings**" }-- "**accounts**" "**shop_ecwid_listings**" }-- "**accounts**"
"**shop_ecwid_listings**" ||-|| "**shop_ecwid**" "**shop_ecwid_listings**" ||-|| "**shop_ecwid**"
@@ -774,6 +896,8 @@ entity "**sync_groups**" {
"**shop_etsy**" }-- "**public.oauth_users**" "**shop_etsy**" }-- "**public.oauth_users**"
"**shop_etsy_events**" ||-|| "**raw_shop_events**"
"**shop_etsy_listings**" }-- "**accounts**" "**shop_etsy_listings**" }-- "**accounts**"
"**shop_etsy_listings**" ||-|| "**shop_etsy**" "**shop_etsy_listings**" ||-|| "**shop_etsy**"
@@ -804,6 +928,8 @@ entity "**sync_groups**" {
"**shop_shopify**" }-- "**public.oauth_users**" "**shop_shopify**" }-- "**public.oauth_users**"
"**shop_shopify_events**" ||-|| "**raw_shop_events**"
"**shop_shopify_listings**" }-- "**accounts**" "**shop_shopify_listings**" }-- "**accounts**"
"**shop_shopify_listings**" ||-|| "**shop_shopify**" "**shop_shopify_listings**" ||-|| "**shop_shopify**"
@@ -834,6 +960,8 @@ entity "**sync_groups**" {
"**shop_square_online**" }-- "**public.oauth_users**" "**shop_square_online**" }-- "**public.oauth_users**"
"**shop_square_online_events**" ||-|| "**raw_shop_events**"
"**shop_square_online_listings**" }-- "**accounts**" "**shop_square_online_listings**" }-- "**accounts**"
"**shop_square_online_listings**" ||-|| "**shop_square_online**" "**shop_square_online_listings**" ||-|| "**shop_square_online**"
@@ -864,6 +992,8 @@ entity "**sync_groups**" {
"**shop_squarespace**" }-- "**public.oauth_users**" "**shop_squarespace**" }-- "**public.oauth_users**"
"**shop_squarespace_events**" ||-|| "**raw_shop_events**"
"**shop_squarespace_listings**" }-- "**accounts**" "**shop_squarespace_listings**" }-- "**accounts**"
"**shop_squarespace_listings**" ||-|| "**shop_squarespace**" "**shop_squarespace_listings**" ||-|| "**shop_squarespace**"
@@ -894,6 +1024,8 @@ entity "**sync_groups**" {
"**shop_tiktok**" }-- "**public.oauth_users**" "**shop_tiktok**" }-- "**public.oauth_users**"
"**shop_tiktok_events**" ||-|| "**raw_shop_events**"
"**shop_tiktok_listings**" }-- "**accounts**" "**shop_tiktok_listings**" }-- "**accounts**"
"**shop_tiktok_listings**" ||-|| "**shop_tiktok**" "**shop_tiktok_listings**" ||-|| "**shop_tiktok**"
@@ -924,6 +1056,8 @@ entity "**sync_groups**" {
"**shop_walmart_marketplace**" }-- "**public.oauth_users**" "**shop_walmart_marketplace**" }-- "**public.oauth_users**"
"**shop_walmart_marketplace_events**" ||-|| "**raw_shop_events**"
"**shop_walmart_marketplace_listings**" }-- "**accounts**" "**shop_walmart_marketplace_listings**" }-- "**accounts**"
"**shop_walmart_marketplace_listings**" ||-|| "**shop_walmart_marketplace**" "**shop_walmart_marketplace_listings**" ||-|| "**shop_walmart_marketplace**"
@@ -954,6 +1088,8 @@ entity "**sync_groups**" {
"**shop_wix**" }-- "**public.oauth_users**" "**shop_wix**" }-- "**public.oauth_users**"
"**shop_wix_events**" ||-|| "**raw_shop_events**"
"**shop_wix_listings**" }-- "**accounts**" "**shop_wix_listings**" }-- "**accounts**"
"**shop_wix_listings**" ||-|| "**shop_wix**" "**shop_wix_listings**" ||-|| "**shop_wix**"
@@ -984,6 +1120,8 @@ entity "**sync_groups**" {
"**shop_woo_commerce**" }-- "**public.oauth_users**" "**shop_woo_commerce**" }-- "**public.oauth_users**"
"**shop_woo_commerce_events**" ||-|| "**raw_shop_events**"
"**shop_woo_commerce_listings**" }-- "**accounts**" "**shop_woo_commerce_listings**" }-- "**accounts**"
"**shop_woo_commerce_listings**" ||-|| "**shop_woo_commerce**" "**shop_woo_commerce_listings**" ||-|| "**shop_woo_commerce**"
@@ -1014,6 +1152,8 @@ entity "**sync_groups**" {
"**shop_zoho**" }-- "**public.oauth_users**" "**shop_zoho**" }-- "**public.oauth_users**"
"**shop_zoho_events**" ||-|| "**raw_shop_events**"
"**shop_zoho_listings**" }-- "**accounts**" "**shop_zoho_listings**" }-- "**accounts**"
"**shop_zoho_listings**" ||-|| "**shop_zoho**" "**shop_zoho_listings**" ||-|| "**shop_zoho**"
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 61 KiB

+8
View File
@@ -51,6 +51,12 @@ entity "**etsy_users**" {
*""shop_id"": //integer // *""shop_id"": //integer //
} }
entity "**mock_mode**" {
+ ""account_id"": //integer [PK][FK]//
--
*""mock_mode"": //boolean //
}
entity "**oauth_login_states**" { entity "**oauth_login_states**" {
+ ""state"": //bytea [PK]// + ""state"": //bytea [PK]//
-- --
@@ -149,6 +155,8 @@ entity "**wix_store_events**" {
"**etsy_users**" }-- "**accounts**" "**etsy_users**" }-- "**accounts**"
"**mock_mode**" ||-|| "**accounts**"
"**oauth_tokens**" }-- "**oauth_users**" "**oauth_tokens**" }-- "**oauth_users**"
"**sync_group_listing_drafts**" }-- "**accounts**" "**sync_group_listing_drafts**" }-- "**accounts**"
+294
View File
@@ -5,11 +5,13 @@ package accounts
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"slices" "slices"
"strings" "strings"
"github.com/google/uuid"
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgxpool" "github.com/jackc/pgx/v5/pgxpool"
@@ -1461,6 +1463,290 @@ func (db *Store) DeleteListingInListingInMockSyncGroupBeingEdited(ctx context.Co
return nil return nil
} }
// if err := s.accts.SetShopInMockSaleForm(c, acctID, platform, shopID); err != nil {
func (db *Store) SetShopInMockSaleForm(ctx context.Context, acctID int64, platform Platform, shopID string) error {
_, err := db.db.Exec(
ctx,
`
INSERT INTO
mock.sale_form_shop_selection (
account_id,
platform,
shop_id
)
VALUES (
@account_id,
@platform,
@shop_id
)
ON CONFLICT (account_id) DO UPDATE
SET platform = EXCLUDED.platform,
shop_id = EXCLUDED.shop_id
`,
pgx.NamedArgs{
"account_id": acctID,
"platform": platform,
"shop_id": shopID,
},
)
if err != nil {
return fmt.Errorf("failed to perform query: %w", err)
}
return nil
}
func (db *Store) SetShopInMockRefundForm(ctx context.Context, acctID int64, platform Platform, shopID string) error {
_, err := db.db.Exec(
ctx,
`
INSERT INTO
mock.refund_form_shop_selection (
account_id,
platform,
shop_id
)
VALUES (
@account_id,
@platform,
@shop_id
)
ON CONFLICT (account_id) DO UPDATE
SET platform = EXCLUDED.platform,
shop_id = EXCLUDED.shop_id
`,
pgx.NamedArgs{
"account_id": acctID,
"platform": platform,
"shop_id": shopID,
},
)
if err != nil {
return fmt.Errorf("failed to perform query: %w", err)
}
return nil
}
func (db *Store) SetShopInMockCountForm(ctx context.Context, acctID int64, platform Platform, shopID string) error {
_, err := db.db.Exec(
ctx,
`
INSERT INTO
mock.count_form_shop_selection (
account_id,
platform,
shop_id
)
VALUES (
@account_id,
@platform,
@shop_id
)
ON CONFLICT (account_id) DO UPDATE
SET platform = EXCLUDED.platform,
shop_id = EXCLUDED.shop_id
`,
pgx.NamedArgs{
"account_id": acctID,
"platform": platform,
"shop_id": shopID,
},
)
if err != nil {
return fmt.Errorf("failed to perform query: %w", err)
}
return nil
}
type MockEventFormValues struct {
SaleShopID string
SalePlatform Platform
RefundShopID string
RefundPlatform Platform
CountShopID string
CountPlatform Platform
}
func (db *Store) GetMockEventFormValues(ctx context.Context, acctID int64) (*MockEventFormValues, error) {
rows, err := db.db.Query(
ctx,
`
SELECT
sf.platform AS sale_platform,
sf.shop_id AS sale_shop_id,
rf.platform AS refund_platform,
rf.shop_id AS refund_shop_id,
cf.platform AS count_platform,
cf.shop_id AS count_shop_id
FROM
mock.sale_form_shop_selection AS sf
FULL OUTER JOIN
mock.refund_form_shop_selection AS rf
USING
(account_id)
FULL OUTER JOIN
mock.count_form_shop_selection AS cf
USING
(account_id)
WHERE
sf.account_id = @account_id
`,
pgx.NamedArgs{
"account_id": acctID,
},
)
if err != nil {
return nil, fmt.Errorf("failed to perform query: %w", err)
}
v, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByNameLax[struct {
Sale_shop_id pgtype.Text
Sale_platform *Platform
Refund_shop_id pgtype.Text
Refund_platform *Platform
Count_shop_id pgtype.Text
Count_platform *Platform
}])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return &MockEventFormValues{}, nil
}
return nil, fmt.Errorf("failed to scan rows: %w", err)
}
return &MockEventFormValues{
SaleShopID: v.Sale_shop_id.String,
SalePlatform: deref(v.Sale_platform),
RefundShopID: v.Refund_shop_id.String,
RefundPlatform: deref(v.Refund_platform),
CountShopID: v.Count_shop_id.String,
CountPlatform: deref(v.Count_platform),
}, nil
}
func (db *Store) SaveNewMockSale(ctx context.Context, acctID int64, platform Platform, shopID, listingID string, count int) (uuid.UUID, error) {
eventID := uuid.New()
_, err := db.db.Exec(
ctx,
`
INSERT INTO
mock.raw_shop_events (
platform,
shop_id,
event_timestamp,
event_id,
raw_payload
)
VALUES (
@platform,
@shop_id,
NOW(),
@event_id,
@raw_payload
)
`,
pgx.NamedArgs{
"platform": platform,
"shop_id": shopID,
"event_id": eventID,
"raw_payload": json.RawMessage(fmt.Sprintf(`{
"type": "sale",
"listingID": %q,
"count": %d
}`, listingID, count)),
},
)
if err != nil {
return uuid.Nil, fmt.Errorf("failed to perform query: %w", err)
}
return eventID, nil
}
func (db *Store) SaveNewMockRefund(ctx context.Context, acctID int64, platform Platform, shopID, listingID string, count int) (uuid.UUID, error) {
eventID := uuid.New()
_, err := db.db.Exec(
ctx,
`
INSERT INTO
mock.raw_shop_events (
platform,
shop_id,
event_timestamp,
event_id,
raw_payload
)
VALUES (
@platform,
@shop_id,
NOW(),
@event_id,
@raw_payload
)
`,
pgx.NamedArgs{
"platform": platform,
"shop_id": shopID,
"event_id": eventID,
"raw_payload": json.RawMessage(fmt.Sprintf(`{
"type": "refund",
"listingID": %q,
"count": %d
}`, listingID, count)),
},
)
if err != nil {
return uuid.Nil, fmt.Errorf("failed to perform query: %w", err)
}
return eventID, nil
}
func (db *Store) SaveNewMockInventoryReset(ctx context.Context, acctID int64, platform Platform, shopID, listingID string, count int) (uuid.UUID, error) {
eventID := uuid.New()
_, err := db.db.Exec(
ctx,
`
INSERT INTO
mock.raw_shop_events (
platform,
shop_id,
event_timestamp,
event_id,
raw_payload
)
VALUES (
@platform,
@shop_id,
NOW(),
@event_id,
@raw_payload
)
`,
pgx.NamedArgs{
"platform": platform,
"shop_id": shopID,
"event_id": eventID,
"raw_payload": json.RawMessage(fmt.Sprintf(`{
"type": "inventory-reset",
"listingID": %q,
"count": %d
}`, listingID, count)),
},
)
if err != nil {
return uuid.Nil, fmt.Errorf("failed to perform query: %w", err)
}
return eventID, nil
}
// additional context // additional context
func (db *Store) GetAccountPointerByUserID(ctx context.Context, userID string) (*Account, error) { func (db *Store) GetAccountPointerByUserID(ctx context.Context, userID string) (*Account, error) {
@@ -1481,3 +1767,11 @@ func (db *Store) beginReadonlyTxn(ctx context.Context, cb func(pgx.Tx) error) er
func (db *Store) beginTxn(ctx context.Context, cb func(pgx.Tx) error) error { func (db *Store) beginTxn(ctx context.Context, cb func(pgx.Tx) error) error {
return pgx.BeginFunc(ctx, db.db, cb) return pgx.BeginFunc(ctx, db.db, cb)
} }
func deref[T any](ptr *T) T {
if ptr != nil {
return *ptr
}
var zero T
return zero
}
+16
View File
@@ -105,6 +105,22 @@ func (v_ctx *StoreWithContext) DeleteListingInListingInMockSyncGroupBeingEdited(
return v_ctx.Store.DeleteListingInListingInMockSyncGroupBeingEdited(v_ctx.ctx, acctID, orderIndex) return v_ctx.Store.DeleteListingInListingInMockSyncGroupBeingEdited(v_ctx.ctx, acctID, orderIndex)
} }
func (v_ctx *StoreWithContext) SetShopInMockSaleForm(acctID int64, platform Platform, shopID string) error {
return v_ctx.Store.SetShopInMockSaleForm(v_ctx.ctx, acctID, platform, shopID)
}
func (v_ctx *StoreWithContext) SetShopInMockRefundForm(acctID int64, platform Platform, shopID string) error {
return v_ctx.Store.SetShopInMockRefundForm(v_ctx.ctx, acctID, platform, shopID)
}
func (v_ctx *StoreWithContext) SetShopInMockCountForm(acctID int64, platform Platform, shopID string) error {
return v_ctx.Store.SetShopInMockCountForm(v_ctx.ctx, acctID, platform, shopID)
}
func (v_ctx *StoreWithContext) GetMockEventFormValues(acctID int64) (*MockEventFormValues, error) {
return v_ctx.Store.GetMockEventFormValues(v_ctx.ctx, acctID)
}
func (v_ctx *StoreWithContext) GetAccountPointerByUserID(userID string) (*Account, error) { func (v_ctx *StoreWithContext) GetAccountPointerByUserID(userID string) (*Account, error) {
return v_ctx.Store.GetAccountPointerByUserID(v_ctx.ctx, userID) return v_ctx.Store.GetAccountPointerByUserID(v_ctx.ctx, userID)
} }
-8
View File
@@ -1235,11 +1235,3 @@ func (db *Store) listMockSyncGroupListings(ctx context.Context, tx pgx.Tx, acctI
return listings, nil return listings, nil
} }
func deref[T any](ptr *T) T {
if ptr == nil {
var zero T
return zero
}
return *ptr
}
+80
View File
@@ -0,0 +1,80 @@
package reports
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
"github.com/jackc/pgx/v5"
"ruben/inventory2/consts"
"ruben/inventory2/domains/accounts"
)
type (
RawShopEvent struct {
Platform accounts.Platform
ShopID string
EventTimestamp time.Time
EventID string
RawPayload json.RawMessage
}
)
func (db *Store) GetRawShopEvents(ctx context.Context, acctID int64, platform accounts.Platform, shopID string) ([]RawShopEvent, error) {
if _, err := db.accts.GetMockShop(ctx, acctID, platform, shopID); err != nil {
if errors.Is(err, consts.ErrNotFound) {
return nil, fmt.Errorf("shop not found: %w", err)
}
return nil, fmt.Errorf("failed to look up shop: %w", err)
}
rows, err := db.db.Query(
ctx,
`
SELECT
event_timestamp,
event_id,
raw_payload
FROM
mock.raw_shop_events
WHERE
platform = @platform
AND shop_id = @shop_id
ORDER BY
event_timestamp DESC,
event_id ASC
`,
pgx.NamedArgs{
"platform": platform,
"shop_id": shopID,
},
)
if err != nil {
return nil, fmt.Errorf("failed to perform query: %w", err)
}
vs, err := pgx.CollectRows(rows, pgx.RowToStructByNameLax[struct {
Event_timestamp time.Time
Event_id string
Raw_payload json.RawMessage
}])
if err != nil {
return nil, fmt.Errorf("failed to scan rows: %w", err)
}
evts := make([]RawShopEvent, len(vs))
for i, v := range vs {
evts[i] = RawShopEvent{
Platform: platform,
ShopID: shopID,
EventTimestamp: v.Event_timestamp,
EventID: v.Event_id,
RawPayload: v.Raw_payload,
}
}
return evts, nil
}
+32
View File
@@ -0,0 +1,32 @@
package reports
import (
"context"
"github.com/jackc/pgx/v5/pgxpool"
"ruben/inventory2/domains/accounts"
"ruben/inventory2/logging"
)
//go:generate concurry -s Store
type (
Store struct {
log *logging.Logger
db *pgxpool.Pool
accts *accounts.Store
}
)
func NewStore(logger *logging.Logger, db *pgxpool.Pool, accts *accounts.Store) *Store {
return &Store{
log: logger,
db: db,
accts: accts,
}
}
func (db *Store) WithContext(ctx context.Context) *StoreWithContext {
return NewStoreWithContext(ctx, db)
}
+25
View File
@@ -0,0 +1,25 @@
// Code generated by concurry DO NOT EDIT.
// https://github.com/angelbeltran/concurry
// concurry
package reports
import (
"context"
"ruben/inventory2/domains/accounts"
)
type StoreWithContext struct {
ctx context.Context
*Store
}
func NewStoreWithContext(ctx context.Context, v *Store) *StoreWithContext {
return &StoreWithContext{
ctx: ctx,
Store: v,
}
}
func (v_ctx *StoreWithContext) GetRawShopEvents(acctID int64, platform accounts.Platform, shopID string) ([]RawShopEvent, error) {
return v_ctx.Store.GetRawShopEvents(v_ctx.ctx, acctID, platform, shopID)
}
+5 -1
View File
@@ -25,6 +25,7 @@ import (
"ruben/inventory2/domains/authentication" "ruben/inventory2/domains/authentication"
etsy_platform "ruben/inventory2/domains/platforms/etsy" etsy_platform "ruben/inventory2/domains/platforms/etsy"
"ruben/inventory2/domains/raw_events" "ruben/inventory2/domains/raw_events"
"ruben/inventory2/domains/reports"
"ruben/inventory2/logging" "ruben/inventory2/logging"
"ruben/inventory2/server" "ruben/inventory2/server"
) )
@@ -151,11 +152,14 @@ func runAuthProcesses(ctx context.Context, auth *authentication.Authenticator) <
} }
func runServer(ctx context.Context, logger *logging.Logger, connPool *pgxpool.Pool, auth *authentication.Authenticator) <-chan error { func runServer(ctx context.Context, logger *logging.Logger, connPool *pgxpool.Pool, auth *authentication.Authenticator) <-chan error {
accts := accounts.NewStore(logger, connPool)
r := server.NewRouter( r := server.NewRouter(
logger.WithGroup("server"), logger.WithGroup("server"),
"./", "./",
raw_events.NewStore(logger.WithGroup("raw-event-store"), connPool), raw_events.NewStore(logger.WithGroup("raw-event-store"), connPool),
accounts.NewStore(logger, connPool), accts,
reports.NewStore(logger, connPool, accts),
etsy_platform.NewPlatform( etsy_platform.NewPlatform(
logger, logger,
func(acctID int64) string { func(acctID int64) string {
+152
View File
@@ -78,6 +78,14 @@ func Routes(
mockSyncGroupBeingEdited.PUT("/listings/:orderIndex/shop", response.Handler(as.setShopInListingInMockSyncGroupBeingEdited)) mockSyncGroupBeingEdited.PUT("/listings/:orderIndex/shop", response.Handler(as.setShopInListingInMockSyncGroupBeingEdited))
mockSyncGroupBeingEdited.PUT("/listings/:orderIndex/listing", response.Handler(as.setListingInListingInMockSyncGroupBeingEdited)) mockSyncGroupBeingEdited.PUT("/listings/:orderIndex/listing", response.Handler(as.setListingInListingInMockSyncGroupBeingEdited))
mockSyncGroupBeingEdited.DELETE("/listings/:orderIndex", response.Handler(as.deleteListingInListingInMockSyncGroupBeingEdited)) mockSyncGroupBeingEdited.DELETE("/listings/:orderIndex", response.Handler(as.deleteListingInListingInMockSyncGroupBeingEdited))
simulations := r.Group("/:acctID/simulations", pub.Publish("/:acctID/simulations"))
simulations.PUT("/sale/form/shop", response.Handler(as.setShopInMockSaleForm))
simulations.POST("/sale", response.Handler(as.postNewMockSale))
simulations.PUT("/refund/form/shop", response.Handler(as.setShopInMockRefundForm))
simulations.POST("/refund", response.Handler(as.postNewMockRefund))
simulations.PUT("/count/form/shop", response.Handler(as.setShopInMockCountForm))
simulations.PUT("/shops/:shopID/listings/:listingID/count", response.Handler(as.setMockListingCount))
} }
// POST / // POST /
@@ -736,6 +744,150 @@ func (s *accountSubrouter) deleteListingInListingInMockSyncGroupBeingEdited(c *g
return response.StatusOK(), nil return response.StatusOK(), nil
} }
// PUT /:acctID/simulations/sale/form/shop
func (s *accountSubrouter) setShopInMockSaleForm(c *gin.Context) (response.Response, error) {
acctID := auth.GetIdentity(c).Account.AccountID
var (
platform accounts.Platform
shopID string
)
if err := param.Form("platform", param.Platform(&platform)).
Form("shop-id", param.Text(&shopID)).
Unmarshal(c); err != nil {
return nil, err
}
if err := s.accts.SetShopInMockSaleForm(c, acctID, platform, shopID); err != nil {
return nil, fmt.Errorf("failed to set shop in mock sale form: %w", err)
}
return response.StatusOK(), nil
}
// POST /:acctID/simulations/sale
func (s *accountSubrouter) postNewMockSale(c *gin.Context) (response.Response, error) {
acctID := auth.GetIdentity(c).Account.AccountID
var (
platform accounts.Platform
shopID string
listingID string
count int
)
if err := param.Form("platform", param.Platform(&platform)).
Form("shop-id", param.Text(&shopID)).
Form("listing-id", param.Text(&listingID)).
Form("count", param.Int(&count)).
Unmarshal(c); err != nil {
return nil, err
}
if _, err := s.accts.SaveNewMockSale(c, acctID, platform, shopID, listingID, count); err != nil {
return nil, fmt.Errorf("failed to save new mock sale: %w", err)
}
return response.StatusCreated(), nil
}
// PUT /:acctID/simulations/refund/form/shop
func (s *accountSubrouter) setShopInMockRefundForm(c *gin.Context) (response.Response, error) {
acctID := auth.GetIdentity(c).Account.AccountID
var (
platform accounts.Platform
shopID string
)
if err := param.Form("platform", param.Platform(&platform)).
Form("shop-id", param.Text(&shopID)).
Unmarshal(c); err != nil {
return nil, err
}
if err := s.accts.SetShopInMockRefundForm(c, acctID, platform, shopID); err != nil {
return nil, fmt.Errorf("failed to set shop in mock refund form: %w", err)
}
return response.StatusOK(), nil
}
// POST /:acctID/simulations/refund
func (s *accountSubrouter) postNewMockRefund(c *gin.Context) (response.Response, error) {
acctID := auth.GetIdentity(c).Account.AccountID
var (
platform accounts.Platform
shopID string
listingID string
count int
)
if err := param.Form("platform", param.Platform(&platform)).
Form("shop-id", param.Text(&shopID)).
Form("listing-id", param.Text(&listingID)).
Form("count", param.Int(&count)).
Unmarshal(c); err != nil {
return nil, err
}
if _, err := s.accts.SaveNewMockRefund(c, acctID, platform, shopID, listingID, count); err != nil {
return nil, fmt.Errorf("failed to save new mock refund: %w", err)
}
return response.StatusCreated(), nil
}
// PUT /:acctID/simulations/count/form/shop
func (s *accountSubrouter) setShopInMockCountForm(c *gin.Context) (response.Response, error) {
acctID := auth.GetIdentity(c).Account.AccountID
var (
platform accounts.Platform
shopID string
)
if err := param.Form("platform", param.Platform(&platform)).
Form("shop-id", param.Text(&shopID)).
Unmarshal(c); err != nil {
return nil, err
}
if err := s.accts.SetShopInMockCountForm(c, acctID, platform, shopID); err != nil {
return nil, fmt.Errorf("failed to set shop in mock count form: %w", err)
}
return response.StatusOK(), nil
}
// PUT /:acctID/simulations/shops/:shopID/listings/:listingID/count
func (s *accountSubrouter) setMockListingCount(c *gin.Context) (response.Response, error) {
acctID := auth.GetIdentity(c).Account.AccountID
var (
platform accounts.Platform
shopID string
listingID string
count int
)
if err := param.Form("platform", param.Platform(&platform)).
Path("shopID", param.Text(&shopID)).
Path("listingID", param.Text(&listingID)).
Form("count", param.Int(&count)).
Unmarshal(c); err != nil {
return nil, err
}
if _, err := s.accts.SaveNewMockInventoryReset(c, acctID, platform, shopID, listingID, count); err != nil {
return nil, fmt.Errorf("failed to save new mock inventory reset: %w", err)
}
return response.StatusOK(), nil
}
func lowerSnakeCase(s accounts.Platform) string { func lowerSnakeCase(s accounts.Platform) string {
return strings.ToLower(strings.Join(strings.Split(string(s), " "), "_")) return strings.ToLower(strings.Join(strings.Split(string(s), " "), "_"))
} }
+3
View File
@@ -12,6 +12,7 @@ import (
"ruben/inventory2/domains/authentication" "ruben/inventory2/domains/authentication"
etsy_platform "ruben/inventory2/domains/platforms/etsy" etsy_platform "ruben/inventory2/domains/platforms/etsy"
"ruben/inventory2/domains/raw_events" "ruben/inventory2/domains/raw_events"
"ruben/inventory2/domains/reports"
"ruben/inventory2/logging" "ruben/inventory2/logging"
"ruben/inventory2/server/api" "ruben/inventory2/server/api"
"ruben/inventory2/server/auth" "ruben/inventory2/server/auth"
@@ -30,6 +31,7 @@ func NewRouter(
contentDir string, contentDir string,
rawEvents *raw_events.Store, rawEvents *raw_events.Store,
accts *accounts.Store, accts *accounts.Store,
reps *reports.Store,
etsy *etsy_platform.Platform, etsy *etsy_platform.Platform,
authr *authentication.Authenticator, authr *authentication.Authenticator,
) *Router { ) *Router {
@@ -59,6 +61,7 @@ func NewRouter(
"/ui", "/ui",
rawEvents, rawEvents,
accts, accts,
reps,
etsy, etsy,
authM.Authenticate(), authM.Authenticate(),
) )
+17 -1
View File
@@ -16,6 +16,7 @@ import (
"ruben/inventory2/domains/accounts" "ruben/inventory2/domains/accounts"
etsy_platform "ruben/inventory2/domains/platforms/etsy" etsy_platform "ruben/inventory2/domains/platforms/etsy"
"ruben/inventory2/domains/raw_events" "ruben/inventory2/domains/raw_events"
"ruben/inventory2/domains/reports"
"ruben/inventory2/logging" "ruben/inventory2/logging"
"ruben/inventory2/server/auth" "ruben/inventory2/server/auth"
"ruben/inventory2/server/response" "ruben/inventory2/server/response"
@@ -28,6 +29,7 @@ type (
templater *templater.Templater templater *templater.Templater
rawEvents *raw_events.Store rawEvents *raw_events.Store
accts *accounts.Store accts *accounts.Store
reports *reports.Store
etsy *etsy_platform.Platform etsy *etsy_platform.Platform
} }
@@ -44,6 +46,7 @@ func Routes(
uiPath string, uiPath string,
rawEvents *raw_events.Store, rawEvents *raw_events.Store,
accts *accounts.Store, accts *accounts.Store,
reps *reports.Store,
etsy *etsy_platform.Platform, etsy *etsy_platform.Platform,
authenticate gin.HandlerFunc, authenticate gin.HandlerFunc,
) { ) {
@@ -143,6 +146,7 @@ func Routes(
}), }),
rawEvents: rawEvents, rawEvents: rawEvents,
accts: accts, accts: accts,
reports: reps,
etsy: etsy, etsy: etsy,
} }
@@ -170,6 +174,14 @@ func (s *webpageRouter) serveTemplate(c *gin.Context) (response.Response, error)
r := c.Request r := c.Request
ctx := r.Context() ctx := r.Context()
id := auth.GetIdentity(ctx)
var mockMode bool
if id.Account != nil {
var err error
if mockMode, err = s.accts.GetMockMode(ctx, id.Account.AccountID); err != nil {
return nil, err
}
}
args := []any{ args := []any{
"Request", "Request",
@@ -181,12 +193,16 @@ func (s *webpageRouter) serveTemplate(c *gin.Context) (response.Response, error)
newURLCalculator(r.URL), newURLCalculator(r.URL),
"Accounts", "Accounts",
s.accts.WithContext(ctx), s.accts.WithContext(ctx),
"Reports",
s.reports.WithContext(ctx),
"Etsy", "Etsy",
s.etsy.WithContext(ctx), s.etsy.WithContext(ctx),
"MockMode",
mockMode,
// auth tooling // auth tooling
"Identity", "Identity",
auth.GetIdentity(ctx), id,
"Auth", "Auth",
newTemplateAuthenticator(r), newTemplateAuthenticator(r),
} }
+52 -74
View File
@@ -197,29 +197,6 @@
.bottom-0 { .bottom-0 {
bottom: calc(var(--spacing) * 0); bottom: calc(var(--spacing) * 0);
} }
.grid-table-\[1fr_1fr_1fr_1fr_1fr_1fr\] {
&:is(table) {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
& > :is(thead, tbody, tfoot) {
grid-column-start: 1;
grid-column-end: -1;
display: grid;
grid-template-columns: subgrid;
& > tr {
grid-column-start: 1;
grid-column-end: -1;
display: grid;
grid-template-columns: subgrid;
& > th, & > td {
grid-column: span 1;
align-content: center;
text-align: center;
}
}
}
}
}
.grid-table-\[1fr_1fr_1fr_1fr_1fr_1fr_1fr\] { .grid-table-\[1fr_1fr_1fr_1fr_1fr_1fr_1fr\] {
&:is(table) { &:is(table) {
display: grid; display: grid;
@@ -273,6 +250,9 @@
max-width: 96rem; max-width: 96rem;
} }
} }
.m-0 {
margin: calc(var(--spacing) * 0);
}
.m-\[0\.5em\] { .m-\[0\.5em\] {
margin: 0.5em; margin: 0.5em;
} }
@@ -294,6 +274,9 @@
.my-\[0\.5em\] { .my-\[0\.5em\] {
margin-block: 0.5em; margin-block: 0.5em;
} }
.my-\[1em\] {
margin-block: 1em;
}
.my-\[1rem\] { .my-\[1rem\] {
margin-block: 1rem; margin-block: 1rem;
} }
@@ -390,9 +373,6 @@
.flex-shrink { .flex-shrink {
flex-shrink: 1; flex-shrink: 1;
} }
.flex-grow {
flex-grow: 1;
}
.flex-grow-\[1\] { .flex-grow-\[1\] {
flex-grow: 1; flex-grow: 1;
} }
@@ -408,12 +388,6 @@
.basis-full { .basis-full {
flex-basis: 100%; flex-basis: 100%;
} }
.border-collapse {
border-collapse: collapse;
}
.transform {
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
}
.animate-pulse { .animate-pulse {
animation: var(--animate-pulse); animation: var(--animate-pulse);
} }
@@ -462,6 +436,9 @@
.gap-\[0\.25em\] { .gap-\[0\.25em\] {
gap: 0.25em; gap: 0.25em;
} }
.gap-\[0\.75em\] {
gap: 0.75em;
}
.gap-\[1em\] { .gap-\[1em\] {
gap: 1em; gap: 1em;
} }
@@ -506,6 +483,10 @@
border-style: var(--tw-border-style); border-style: var(--tw-border-style);
border-width: 1px; border-width: 1px;
} }
.border-\[2px\] {
border-style: var(--tw-border-style);
border-width: 2px;
}
.border-b-\[2px\] { .border-b-\[2px\] {
border-bottom-style: var(--tw-border-style); border-bottom-style: var(--tw-border-style);
border-bottom-width: 2px; border-bottom-width: 2px;
@@ -514,6 +495,12 @@
--tw-border-style: solid; --tw-border-style: solid;
border-style: solid; border-style: solid;
} }
.border-\[gray\] {
border-color: gray;
}
.border-border {
border-color: var(--border);
}
.border-foreground { .border-foreground {
border-color: var(--foreground); border-color: var(--foreground);
} }
@@ -553,6 +540,9 @@
.py-\[1em\] { .py-\[1em\] {
padding-block: 1em; padding-block: 1em;
} }
.py-\[2em\] {
padding-block: 2em;
}
.pt-\[1em\] { .pt-\[1em\] {
padding-top: 1em; padding-top: 1em;
} }
@@ -574,9 +564,6 @@
.font-display { .font-display {
font-family: var(--display-family); font-family: var(--display-family);
} }
.font-text {
font-family: var(--text-family);
}
.text-lg { .text-lg {
font-size: var(--text-lg); font-size: var(--text-lg);
line-height: var(--tw-leading, var(--text-lg--line-height)); line-height: var(--tw-leading, var(--text-lg--line-height));
@@ -592,10 +579,6 @@
.text-\[1\.5em\] { .text-\[1\.5em\] {
font-size: 1.5em; font-size: 1.5em;
} }
.font-\[1\.5em\] {
--tw-font-weight: 1.5em;
font-weight: 1.5em;
}
.font-bold { .font-bold {
--tw-font-weight: var(--font-weight-bold); --tw-font-weight: var(--font-weight-bold);
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
@@ -607,12 +590,6 @@
.text-nowrap { .text-nowrap {
text-wrap: nowrap; text-wrap: nowrap;
} }
.text-wrap {
text-wrap: wrap;
}
.text-inherit {
color: inherit;
}
.capitalize { .capitalize {
text-transform: capitalize; text-transform: capitalize;
} }
@@ -622,10 +599,6 @@
.underline { .underline {
text-decoration-line: underline; text-decoration-line: underline;
} }
.outline {
outline-style: var(--tw-outline-style);
outline-width: 1px;
}
.outline-1 { .outline-1 {
outline-style: var(--tw-outline-style); outline-style: var(--tw-outline-style);
outline-width: 1px; outline-width: 1px;
@@ -852,6 +825,11 @@
} }
} }
} }
.active\:bg-accent-secondary {
&:active {
background-color: var(--accent-secondary);
}
}
.disabled\:cursor-not-allowed { .disabled\:cursor-not-allowed {
&:disabled { &:disabled {
cursor: not-allowed; cursor: not-allowed;
@@ -1200,26 +1178,6 @@
} }
} }
} }
@property --tw-rotate-x {
syntax: "*";
inherits: false;
}
@property --tw-rotate-y {
syntax: "*";
inherits: false;
}
@property --tw-rotate-z {
syntax: "*";
inherits: false;
}
@property --tw-skew-x {
syntax: "*";
inherits: false;
}
@property --tw-skew-y {
syntax: "*";
inherits: false;
}
@property --tw-border-style { @property --tw-border-style {
syntax: "*"; syntax: "*";
inherits: false; inherits: false;
@@ -1302,6 +1260,26 @@
inherits: false; inherits: false;
initial-value: 1; initial-value: 1;
} }
@property --tw-rotate-x {
syntax: "*";
inherits: false;
}
@property --tw-rotate-y {
syntax: "*";
inherits: false;
}
@property --tw-rotate-z {
syntax: "*";
inherits: false;
}
@property --tw-skew-x {
syntax: "*";
inherits: false;
}
@property --tw-skew-y {
syntax: "*";
inherits: false;
}
@property --tw-content { @property --tw-content {
syntax: "*"; syntax: "*";
initial-value: ""; initial-value: "";
@@ -1319,11 +1297,6 @@
@layer properties { @layer properties {
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
*, ::before, ::after, ::backdrop { *, ::before, ::after, ::backdrop {
--tw-rotate-x: initial;
--tw-rotate-y: initial;
--tw-rotate-z: initial;
--tw-skew-x: initial;
--tw-skew-y: initial;
--tw-border-style: solid; --tw-border-style: solid;
--tw-font-weight: initial; --tw-font-weight: initial;
--tw-outline-style: solid; --tw-outline-style: solid;
@@ -1343,6 +1316,11 @@
--tw-scale-x: 1; --tw-scale-x: 1;
--tw-scale-y: 1; --tw-scale-y: 1;
--tw-scale-z: 1; --tw-scale-z: 1;
--tw-rotate-x: initial;
--tw-rotate-y: initial;
--tw-rotate-z: initial;
--tw-skew-x: initial;
--tw-skew-y: initial;
--tw-content: ""; --tw-content: "";
--tw-leading: initial; --tw-leading: initial;
} }
@@ -6,17 +6,27 @@
hx-trigger="sse:accounts_{{$acctID}}_mock-mode" hx-trigger="sse:accounts_{{$acctID}}_mock-mode"
hx-swap="morph" hx-swap="morph"
> >
{{- $mockMode := .Accounts.GetMockMode $acctID }} {{- $mockMode := .MockMode }}
<form id="mock-mode-form"> <form id="mock-mode-form">
<label class="flex items-center gap-[0.5em] font-display text-[1.5em]"> <label
class="
flex
items-center
gap-[0.5em]
font-display
text-[1.5em]
cursor-pointer
w-fit
"
>
Mock Mode Mock Mode
<input <input
type="checkbox" type="checkbox"
{{- if $mockMode }} {{- if $mockMode }}
checked checked
{{- end }} {{- end }}
class="h-[1em] w-[1em]" class="h-[1em] w-[1em] cursor-pointer"
hx-put="/api/accounts/{{$acctID}}/mock-mode" hx-put="/api/accounts/{{$acctID}}/mock-mode"
hx-vals='{"on": {{not $mockMode}}}' hx-vals='{"on": {{not $mockMode}}}'
hx-swap="none" hx-swap="none"
@@ -0,0 +1,136 @@
{{/* [ .Shops, .SelectedPlatform, .SelectedShopID, .Listings ] */}}
{{- $acctID := .Identity.Account.AccountID -}}
{{- $shops := .Shops }}
{{- if not $shops }}
{{- $shops = .Accounts.GetMockShops $acctID -}}
{{- end }}
{{- $selectedPlatform := .SelectedPlatform }}
{{- $selectedShopID := .SelectedShopID }}
{{- if not $selectedPlatform | or (not $selectedShopID) }}
{{- $formSelections := .Accounts.GetMockEventFormValues $acctID }}
{{- $selectedPlatform = $formSelections.RefundPlatform }}
{{- $selectedShopID = $formSelections.RefundShopID }}
{{- end }}
{{- $listings := .Listings }}
{{- if $selectedShopID }}
{{ $listings = .Accounts.GetMockListingsForShop $acctID $selectedPlatform $selectedShopID }}
{{- end }}
{{/* REFUNDS */}}
{{- define "refund-accordion-header" }}
<h2 class="inline-block">
Refund
</h2>
{{ end }}
{{- define "refund-accordion-body" }}
{{ $acctID := .Identity.Account.AccountID }}
{{ $selectedPlatform := .SelectedPlatform }}
{{ $selectedShopID := .SelectedShopID }}
{{ $listings := .Listings }}
{{/* form updater*/}}
<div
hidden
hx-trigger="sse:accounts_{{$acctID}}_simulations_refund"
hx-get="/ui/accounts/{{$acctID}}/simulations/refund-form"
hx-select="#refund-form"
hx-target="#refund-form"
>
</div>
<form
id="refund-form"
class="flex flex-col items-center gap-[0.75em]"
hx-post="/api/accounts/{{$acctID}}/simulations/refund"
hx-swap="none"
hx-vals='js:{
"platform": {{printf "%q" $selectedPlatform}},
"shop-id": {{printf "%q" $selectedShopID}},
}'
>
<select
name="shop-id"
class="bg-background"
required
hx-put="/api/accounts/{{$acctID}}/simulations/refund/form/shop"
hx-vals='js:{
platform: event.target.value.replace(/-[^ ]*/, ""),
"shop-id": event.target.value.replace(/[^ -]*-/, "")
}'
>
<option disabled selected>
- Select A Shop -
</option>
{{- range $shop := .Shops }}
<option
value="{{$shop.Platform}}-{{$shop.ShopID}}"
{{- if and
(eq $shop.Platform $selectedPlatform)
(eq $shop.ShopID $selectedShopID)
}}
selected
{{- end }}
>
{{ $shop.Name }}
</option>
{{- end }}
</select>
<select
name="listing-id"
class="bg-background"
required
>
{{- if $listings }}
<option disabled selected>
- Select A Listing -
</option>
{{- range $listing := $listings }}
<option value="{{$listing.ListingID}}">
{{ $listing.Name }} - {{ $listing.SKU }}
</option>
{{- end }}
{{- else }}
<option disabled selected>
- No Listings Found -
</option>
{{- end }}
</select>
<input
class="p-[0.5em] border-medium border-border rounded-lg bg-background"
type="number"
min="1"
max="1000000000"
name="count"
value="0"
required
/>
<div>
{{ component "button"
"Text" "Submit"
"Background" true
"Class" "m-0"
}}
</div>
</form>
{{ end }}
{{ component "accordion"
"Name" "simulate"
"Shops" $shops
"SelectedPlatform" $selectedPlatform
"SelectedShopID" $selectedShopID
"Listings" $listings
"Class" `
mx-[1em]
mb-[1em]
`
"#accordion-header" "refund-accordion-header"
"#accordion-body" "refund-accordion-body"
}}
@@ -0,0 +1,136 @@
{{/* [ .Shops, .SelectedPlatform, .SelectedShopID, .Listings ] */}}
{{- $acctID := .Identity.Account.AccountID -}}
{{- $shops := .Shops }}
{{- if not $shops }}
{{- $shops = .Accounts.GetMockShops $acctID -}}
{{- end }}
{{- $selectedPlatform := .SelectedPlatform }}
{{- $selectedShopID := .SelectedShopID }}
{{- if not $selectedPlatform | or (not $selectedShopID) }}
{{- $formSelections := .Accounts.GetMockEventFormValues $acctID }}
{{- $selectedPlatform = $formSelections.SalePlatform }}
{{- $selectedShopID = $formSelections.SaleShopID }}
{{- end }}
{{- $listings := .Listings }}
{{- if $selectedShopID }}
{{ $listings = .Accounts.GetMockListingsForShop $acctID $selectedPlatform $selectedShopID }}
{{- end }}
{{/* SALES */}}
{{- define "sale-accordion-header" }}
<h2 class="inline-block">
Sale
</h2>
{{ end }}
{{- define "sale-accordion-body" }}
{{ $acctID := .Identity.Account.AccountID }}
{{ $selectedPlatform := .SelectedPlatform }}
{{ $selectedShopID := .SelectedShopID }}
{{ $listings := .Listings }}
{{/* form updater*/}}
<div
hidden
hx-trigger="sse:accounts_{{$acctID}}_simulations_sale"
hx-get="/ui/accounts/{{$acctID}}/simulations/sales-form"
hx-select="#sales-form"
hx-target="#sales-form"
>
</div>
<form
id="sales-form"
class="flex flex-col items-center gap-[0.75em]"
hx-post="/api/accounts/{{$acctID}}/simulations/sale"
hx-swap="none"
hx-vals='js:{
"platform": {{printf "%q" $selectedPlatform}},
"shop-id": {{printf "%q" $selectedShopID}},
}'
>
<select
name="shop-id"
class="bg-background"
required
hx-put="/api/accounts/{{$acctID}}/simulations/sale/form/shop"
hx-vals='js:{
platform: event.target.value.replace(/-[^ ]*/, ""),
"shop-id": event.target.value.replace(/[^ -]*-/, "")
}'
>
<option disabled selected>
- Select A Shop -
</option>
{{- range $shop := .Shops }}
<option
value="{{$shop.Platform}}-{{$shop.ShopID}}"
{{- if and
(eq $shop.Platform $selectedPlatform)
(eq $shop.ShopID $selectedShopID)
}}
selected
{{- end }}
>
{{ $shop.Name }}
</option>
{{- end }}
</select>
<select
name="listing-id"
class="bg-background"
required
>
{{- if $listings }}
<option disabled selected>
- Select A Listing -
</option>
{{- range $listing := $listings }}
<option value="{{$listing.ListingID}}">
{{ $listing.Name }} - {{ $listing.SKU }}
</option>
{{- end }}
{{- else }}
<option disabled selected>
- No Listings Found -
</option>
{{- end }}
</select>
<input
class="p-[0.5em] border-medium border-border rounded-lg bg-background"
type="number"
min="1"
max="1000000000"
name="count"
value="0"
required
/>
<div>
{{ component "button"
"Text" "Submit"
"Background" true
"Class" "m-0"
}}
</div>
</form>
{{ end }}
{{ component "accordion"
"Name" "simulate"
"Shops" $shops
"SelectedPlatform" $selectedPlatform
"SelectedShopID" $selectedShopID
"Listings" $listings
"Class" `
mx-[1em]
mb-[1em]
`
"#accordion-header" "sale-accordion-header"
"#accordion-body" "sale-accordion-body"
}}
@@ -0,0 +1,136 @@
{{/* [ .Shops, .SelectedPlatform, .SelectedShopID, .Listings ] */}}
{{- $acctID := .Identity.Account.AccountID -}}
{{- $shops := .Shops }}
{{- if not $shops }}
{{- $shops = .Accounts.GetMockShops $acctID -}}
{{- end }}
{{- $selectedPlatform := .SelectedPlatform }}
{{- $selectedShopID := .SelectedShopID }}
{{- if not $selectedPlatform | or (not $selectedShopID) }}
{{- $formSelections := .Accounts.GetMockEventFormValues $acctID }}
{{- $selectedPlatform = $formSelections.CountPlatform }}
{{- $selectedShopID = $formSelections.CountShopID }}
{{- end }}
{{- $listings := .Listings }}
{{- if $selectedShopID }}
{{ $listings = .Accounts.GetMockListingsForShop $acctID $selectedPlatform $selectedShopID }}
{{- end }}
{{/* SET INVENTORY */}}
{{- define "set-inventory-accordion-header" }}
<h2 class="inline-block">
Set Inventory
</h2>
{{ end }}
{{- define "set-inventory-accordion-body" }}
{{ $acctID := .Identity.Account.AccountID }}
{{ $selectedPlatform := .SelectedPlatform }}
{{ $selectedShopID := .SelectedShopID }}
{{ $listings := .Listings }}
{{/* form updater*/}}
<div
hidden
hx-trigger="sse:accounts_{{$acctID}}_simulations_shops"
hx-get="/ui/accounts/{{$acctID}}/simulations/set-inventory-form"
hx-select="#set-inventory-form"
hx-target="#set-inventory-form"
>
</div>
<form
id="set-inventory-form"
class="flex flex-col items-center gap-[0.75em]"
hx-put="/api/accounts/{{$acctID}}/simulations/shops/{shop-id}/listings/{listing-id}/count"
hx-swap="none"
hx-vals='js:{
"platform": {{printf "%q" $selectedPlatform}},
"shop-id": {{printf "%q" $selectedShopID}},
}'
>
<select
name="shop-id"
class="bg-background"
required
hx-put="/api/accounts/{{$acctID}}/simulations/count/form/shop"
hx-vals='js:{
platform: event.target.value.replace(/-[^ ]*/, ""),
"shop-id": event.target.value.replace(/[^ -]*-/, "")
}'
>
<option disabled selected>
- Select A Shop -
</option>
{{- range $shop := .Shops }}
<option
value="{{$shop.Platform}}-{{$shop.ShopID}}"
{{- if and
(eq $shop.Platform $selectedPlatform)
(eq $shop.ShopID $selectedShopID)
}}
selected
{{- end }}
>
{{ $shop.Name }}
</option>
{{- end }}
</select>
<select
name="listing-id"
class="bg-background"
required
>
{{- if $listings }}
<option disabled selected>
- Select A Listing -
</option>
{{- range $listing := $listings }}
<option value="{{$listing.ListingID}}">
{{ $listing.Name }} - {{ $listing.SKU }}
</option>
{{- end }}
{{- else }}
<option disabled selected>
- No Listings Found -
</option>
{{- end }}
</select>
<input
class="p-[0.5em] border-medium border-border rounded-lg bg-background"
type="number"
min="1"
max="1000000000"
name="count"
value="0"
required
/>
<div>
{{ component "button"
"Text" "Submit"
"Background" true
"Class" "m-0"
}}
</div>
</form>
{{ end }}
{{ component "accordion"
"Name" "simulate"
"Shops" $shops
"SelectedPlatform" $selectedPlatform
"SelectedShopID" $selectedShopID
"Listings" $listings
"Class" `
mx-[1em]
mb-[1em]
`
"#accordion-header" "set-inventory-accordion-header"
"#accordion-body" "set-inventory-accordion-body"
}}
+2 -1
View File
@@ -4,13 +4,14 @@
<button <button
class=" class="
border-thin border-thin
bg-card {{if .Background}}bg-background{{else}}bg-card{{end}}
rounded-sm rounded-sm
p-[0.5em] p-[0.5em]
font-semibold font-semibold
cursor-pointer cursor-pointer
hover:underline hover:underline
hover:bg-accent hover:bg-accent
active:bg-accent-secondary
disabled:bg-accent-secondary disabled:bg-accent-secondary
disabled:cursor-not-allowed disabled:cursor-not-allowed
disabled:no-underline disabled:no-underline
+9 -5
View File
@@ -1,11 +1,10 @@
{{- $loggedIn := and (and .Identity .Identity.AccessToken) true -}} {{- $loggedIn := and (and .Identity .Identity.AccessToken) true -}}
{{- $hasAccount := and (and .Identity .Identity.Account) true -}} {{- $hasAccount := and (and .Identity .Identity.Account) true -}}
{{- $acctID := 0 }} {{- $acctID := 0 }}
{{- $mockMode := false }}
{{- if $hasAccount }} {{- if $hasAccount }}
{{- $acctID = .Identity.Account.AccountID }} {{- $acctID = .Identity.Account.AccountID }}
{{- $mockMode = .Accounts.GetMockMode $acctID }}
{{- end }} {{- end }}
{{- $mockMode := .MockMode }}
<!DOCTYPE html> <!DOCTYPE html>
@@ -63,6 +62,11 @@
flex flex
justify-center justify-center
{{ if .MockMode }}
border-[2px]
border-[gray]
{{ end }}
"{{ end }} "{{ end }}
{{ define "navbar-ul-class" }}class=" {{ define "navbar-ul-class" }}class="
@@ -108,7 +112,7 @@
> >
<ul <ul
hx-swap="morph:innerHTML" hx-swap="morph:innerHTML"
{{ template "navbar-ul-class" }} {{ template "navbar-ul-class" . }}
> >
{{- if not $loggedIn }} {{- if not $loggedIn }}
@@ -177,7 +181,7 @@
<nav {{ template "navbar-class" . }}> <nav {{ template "navbar-class" . }}>
<ul <ul
hx-swap="morph:innerHTML" hx-swap="morph:innerHTML"
{{ template "navbar-ul-class" }} {{ template "navbar-ul-class" . }}
> >
{{ template "navbar-link" (props {{ template "navbar-link" (props
"Href" (printf "/ui/accounts/%d/shops" $acctID) "Href" (printf "/ui/accounts/%d/shops" $acctID)
@@ -198,7 +202,7 @@
<nav {{ template "navbar-class" . }}> <nav {{ template "navbar-class" . }}>
<ul <ul
hx-swap="morph:innerHTML" hx-swap="morph:innerHTML"
{{ template "navbar-ul-class" }} {{ template "navbar-ul-class" . }}
> >
{{ template "navbar-link" (props {{ template "navbar-link" (props
"Href" (printf "/ui/accounts/%d/inventory" $acctID) "Href" (printf "/ui/accounts/%d/inventory" $acctID)
@@ -0,0 +1,69 @@
{{- $acctID := .Identity.Account.AccountID }}
{{- $platform := .Platform }}
{{- if not $platform }}
{{- $platform = .Request.URL.Query.Get "platform" }}
{{- end }}
{{- $shopID := .ShopID }}
{{- if not $shopID }}
{{- $shopID = .Request.URL.Query.Get "shop-id" }}
{{- end }}
<label for="shop-selector">
Select a Shop
</label>
<select
id="shop-selector"
hx-swap=""
hx-get="/ui/accounts/{{$acctID}}/reports/mock"
hx-vals="js:{
platform: console.log(event.target.value.replace(/-.*/, '')) || event.target.value.replace(/-.*/, ''),
'shop-id': console.log(event.target.value.replace(/[^-]*-/, '')) || event.target.value.replace(/[^-]*-/, ''),
}"
hx-push-url="true"
hx-target="body"
>
{{- range $shop := .Accounts.GetMockShops $acctID }}
{{- $isSelected := and (eq $platform $shop.Platform) (eq $shopID $shop.ShopID) }}
<option
value="{{ $shop.Platform }}-{{ $shop.ShopID }}"
{{ if $isSelected }}selected{{end}}
>
{{ $shop.Name }}
</option>
{{- end }}
</select>
<h3 class="m-[1em]">
Platform: {{ $platform }}
</h3>
{{- $evts := "" }}
{{- if and $platform $shopID }}
{{- $parsedPlatform := parsePlatform $platform }}
{{- $evts = .Reports.GetRawShopEvents $acctID $parsedPlatform $shopID }}
{{- end }}
<table id="raw-event-table">
<thead>
<tr>
<td>Time</td>
{{/*<td>EventID</td>*/}}
<td>Event</td>
</tr>
</thead>
<tbody>
{{- if $evts }}
{{- range $evt := $evts }}
<tr>
<td>{{$evt.EventTimestamp}}</td>
{{/*<td>{{$evt.EventID}}</td>*/}}
<td>{{prettyPrintJSON $evt.RawPayload}}</td>
</tr>
{{- end }}
{{- end }}
</tbody>
</table>
@@ -1,3 +1,48 @@
<h1> {{- $acctID := .Identity.Account.AccountID -}}
Simulations {{- $shops := .Accounts.GetMockShops $acctID -}}
{{- $formSelections := .Accounts.GetMockEventFormValues $acctID -}}
{{- $saleShopListings := "" }}
{{- if $formSelections.SaleShopID }}
{{ $saleShopListings = .Accounts.GetMockListingsForShop $acctID $formSelections.SalePlatform $formSelections.SaleShopID }}
{{- end }}
{{- $refundShopListings := "" }}
{{- if $formSelections.RefundShopID }}
{{ $refundShopListings = .Accounts.GetMockListingsForShop $acctID $formSelections.SalePlatform $formSelections.RefundShopID }}
{{- end }}
{{- $setInventoryShopListings := "" }}
{{- if $formSelections.CountShopID }}
{{ $setInventoryShopListings = .Accounts.GetMockListingsForShop $acctID $formSelections.SalePlatform $formSelections.CountShopID }}
{{- end }}
<h1 class="text-center my-[0.5em]">
Simulate
</h1> </h1>
{{/* SALES */}}
{{ component (printf "/accounts/%d/simulations/sales-form" $acctID)
"Shops" $shops
"SelectedPlatform" $formSelections.SalePlatform
"SelectedShopID" $formSelections.SaleShopID
"Listings" $saleShopListings
}}
{{/* REFUNDS */}}
{{ component (printf "/accounts/%d/simulations/refund-form" $acctID)
"Shops" $shops
"SelectedPlatform" $formSelections.RefundPlatform
"SelectedShopID" $formSelections.RefundShopID
"Listings" $refundShopListings
}}
{{/* SET INVENTORY */}}
{{ component (printf "/accounts/%d/simulations/set-inventory-form" $acctID)
"Shops" $shops
"SelectedPlatform" $formSelections.CountPlatform
"SelectedShopID" $formSelections.CountShopID
"Listings" $setInventoryShopListings
}}