diff --git a/database_migrations/000015_mock_accounts.down.sql b/database_migrations/000015_mock_accounts.down.sql index dabc12b..d0824c1 100644 --- a/database_migrations/000015_mock_accounts.down.sql +++ b/database_migrations/000015_mock_accounts.down.sql @@ -1,6 +1,10 @@ BEGIN; +-- mock etsy sync group listings + +DROP TABLE mock.shop_etsy_sync_group_listings; +DROP TABLE mock.shop_etsy_listings; -- mock ebay sync group listings @@ -71,6 +75,7 @@ DROP TABLE mock.sync_groups; -- mock shops +DROP TABLE mock.shop_etsy; DROP TABLE mock.shop_ebay; DROP TABLE mock.shop_walmart_marketplace; DROP TABLE mock.shop_amazon; diff --git a/database_migrations/000015_mock_accounts.up.sql b/database_migrations/000015_mock_accounts.up.sql index 6e4e126..99fa339 100644 --- a/database_migrations/000015_mock_accounts.up.sql +++ b/database_migrations/000015_mock_accounts.up.sql @@ -138,6 +138,16 @@ CREATE TABLE mock.shop_ebay ( FOREIGN KEY (account_id, user_id) REFERENCES mock.accounts (account_id, user_id) ); +CREATE TABLE mock.shop_etsy ( + user_id TEXT NOT NULL REFERENCES oauth_users (user_id), + account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id), + shop_id TEXT NOT NULL, + name TEXT NOT NULL CHECK (char_length(name) > 0), + + PRIMARY KEY (account_id, shop_id), + FOREIGN KEY (account_id, user_id) REFERENCES mock.accounts (account_id, user_id) +); + -- mock sync group listings @@ -467,6 +477,32 @@ CREATE TABLE mock.shop_ebay_sync_group_listings ( FOREIGN KEY (shop_id, listing_id) REFERENCES mock.shop_ebay_listings (shop_id, listing_id) ); +-- mock etsy sync group listings + +CREATE TABLE mock.shop_etsy_listings ( + account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id), + shop_id TEXT NOT NULL, + listing_id TEXT NOT NULL, + name TEXT NOT NULL, + + PRIMARY KEY (shop_id, listing_id), + FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_etsy (account_id, shop_id) +); + +CREATE TABLE mock.shop_etsy_sync_group_listings ( + account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id), + sync_group_id INTEGER NOT NULL REFERENCES mock.sync_groups (sync_group_id), + shop_id TEXT NOT NULL, + listing_id TEXT NOT NULL, + order_index INTEGER NOT NULL, + + -- allow each shop to have at most ONE listing per sync group + UNIQUE (sync_group_id, shop_id), + FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_etsy (account_id, shop_id), + FOREIGN KEY (sync_group_id, order_index) REFERENCES mock.sync_group_listing_order_indexes (sync_group_id, order_index), + FOREIGN KEY (shop_id, listing_id) REFERENCES mock.shop_etsy_listings (shop_id, listing_id) +); + GRANT ALL ON SCHEMA mock TO PUBLIC; diff --git a/diagrams/database_schema_mock.svg b/diagrams/database_schema_mock.svg index aede540..567e7a9 100644 --- a/diagrams/database_schema_mock.svg +++ b/diagrams/database_schema_mock.svg @@ -1 +1 @@ -accountsaccount_id:serial [PK]user_id:text [FK]shop_amazonaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_amazon_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_amazon_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_big_cartelaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_big_cartel_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_big_cartel_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_ebayaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_ebay_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_ebay_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_ecwidaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_ecwid_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_ecwid_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_shopifyaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_shopify_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_shopify_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_square_onlineaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_square_online_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_square_online_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_squarespaceaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_squarespace_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_squarespace_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_tiktokaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_tiktok_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_tiktok_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_walmart_marketplaceaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_walmart_marketplace_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_walmart_marketplace_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_wixaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_wix_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_wix_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_woo_commerceaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_woo_commerce_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_woo_commerce_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_zohoaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_zoho_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_zoho_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integersync_group_listing_order_indexessync_group_id:integer [FK]order_index:integersync_groupssync_group_id:serial [PK]account_id:integer [FK]name:textpublic.oauth_users \ No newline at end of file +accountsaccount_id:serial [PK]user_id:text [FK]shop_amazonaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_amazon_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_amazon_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_big_cartelaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_big_cartel_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_big_cartel_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_ebayaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_ebay_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_ebay_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_ecwidaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_ecwid_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_ecwid_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_etsyaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_etsy_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_etsy_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_shopifyaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_shopify_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_shopify_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_square_onlineaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_square_online_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_square_online_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_squarespaceaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_squarespace_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_squarespace_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_tiktokaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_tiktok_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_tiktok_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_walmart_marketplaceaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_walmart_marketplace_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_walmart_marketplace_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_wixaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_wix_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_wix_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_woo_commerceaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_woo_commerce_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_woo_commerce_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integershop_zohoaccount_id:integer [PK][FK]shop_id:text [PK]user_id:text [FK]name:textshop_zoho_listingsshop_id:text [PK]listing_id:text [PK]account_id:integer [FK]name:textshop_zoho_sync_group_listingsaccount_id:integer [FK]sync_group_id:integer [FK]shop_id:text [FK]listing_id:textorder_index:integersync_group_listing_order_indexessync_group_id:integer [FK]order_index:integersync_groupssync_group_id:serial [PK]account_id:integer [FK]name:textpublic.oauth_users \ No newline at end of file diff --git a/diagrams/database_schema_mock.uml b/diagrams/database_schema_mock.uml index d94b0d3..922b90f 100644 --- a/diagrams/database_schema_mock.uml +++ b/diagrams/database_schema_mock.uml @@ -109,6 +109,31 @@ entity "**shop_ecwid_sync_group_listings**" { *""order_index"": //integer // } +entity "**shop_etsy**" { + + ""account_id"": //integer [PK][FK]// + + ""shop_id"": //text [PK]// + -- + *""user_id"": //text [FK]// + *""name"": //text // +} + +entity "**shop_etsy_listings**" { + + ""shop_id"": //text [PK]// + + ""listing_id"": //text [PK]// + -- + *""account_id"": //integer [FK]// + *""name"": //text // +} + +entity "**shop_etsy_sync_group_listings**" { + -- + *""account_id"": //integer [FK]// + *""sync_group_id"": //integer [FK]// + *""shop_id"": //text [FK]// + *""listing_id"": //text // + *""order_index"": //integer // +} + entity "**shop_shopify**" { + ""account_id"": //integer [PK][FK]// + ""shop_id"": //text [PK]// @@ -404,6 +429,26 @@ entity "**sync_groups**" { "**shop_ecwid_sync_group_listings**" }-- "**sync_group_listing_order_indexes**" +"**shop_etsy**" }-- "**accounts**" + +"**shop_etsy**" }-- "**accounts**" + +"**shop_etsy**" }-- "**public.oauth_users**" + +"**shop_etsy_listings**" }-- "**accounts**" + +"**shop_etsy_listings**" }-- "**shop_etsy**" + +"**shop_etsy_sync_group_listings**" }-- "**accounts**" + +"**shop_etsy_sync_group_listings**" }-- "**shop_etsy**" + +"**shop_etsy_sync_group_listings**" }-- "**shop_etsy_listings**" + +"**shop_etsy_sync_group_listings**" }-- "**sync_groups**" + +"**shop_etsy_sync_group_listings**" }-- "**sync_group_listing_order_indexes**" + "**shop_shopify**" }-- "**accounts**" "**shop_shopify**" }-- "**accounts**" diff --git a/internal/consts/errors.go b/internal/consts/errors.go index 7b70776..93a2e91 100644 --- a/internal/consts/errors.go +++ b/internal/consts/errors.go @@ -3,6 +3,7 @@ package consts import "errors" var ( - ErrNotFound = errors.New("not found") - ErrConflict = errors.New("conflict") + ErrNotFound = errors.New("not found") + ErrConflict = errors.New("conflict") + ErrBadRequest = errors.New("bad request") ) diff --git a/internal/domains/accounts/accounts.go b/internal/domains/accounts/accounts.go index 53a9c7d..5cf3cdf 100644 --- a/internal/domains/accounts/accounts.go +++ b/internal/domains/accounts/accounts.go @@ -10,6 +10,7 @@ import ( "ruben/inventory2/internal/consts" "ruben/inventory2/internal/logging" + "github.com/google/uuid" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) @@ -59,6 +60,11 @@ type ( AccountShopIDs ListingID string } + + MockShop struct { + AccountShopIDs + Name string + } ) func NewStore(logger *logging.Logger, db *pgxpool.Pool) *Store { @@ -309,6 +315,258 @@ func (db *Store) GetListingsForShop(ctx context.Context, acctID int64, platform return vs, nil } +func (db *Store) CreateMockShop(ctx context.Context, acctID int64, platform Platform, name string) (shopID uuid.UUID, err error) { + shopTableName, err := getMockShopTableName(platform) + if err != nil { + return uuid.Nil, err + } + + tx, err := db.db.Begin(ctx) + if err != nil { + return uuid.Nil, fmt.Errorf("failed to being transaction: %w", err) + } + defer tx.Rollback(ctx) + + // TODO: are we really going to just copy the id between accounts and mock.accounts? + + // upsert mock account + + rows, err := tx.Query( + ctx, + ` + WITH existing_account(account_id, user_id) AS ( + SELECT + account_id, + user_id + FROM + accounts + WHERE + account_id = @account_id + ), existing_mock_account AS ( + SELECT + user_id + FROM + accounts + WHERE + account_id = @account_id + ), inserted_mock_account AS ( + INSERT INTO + mock.accounts ( + account_id, + user_id + ) + SELECT + account_id, + user_id + FROM + existing_account + ON CONFLICT + DO NOTHING + RETURNING + user_id + ) + SELECT + COALESCE(ia.user_id, ea.user_id) + FROM + inserted_mock_account ia + FULL OUTER JOIN + existing_mock_account ea + USING + (user_id) + `, + pgx.NamedArgs{ + "account_id": acctID, + }, + ) + if err != nil { + return uuid.Nil, fmt.Errorf("test query failed: %w", err) + } + + userID, err := pgx.CollectExactlyOneRow(rows, pgx.RowTo[string]) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return uuid.Nil, fmt.Errorf("%w: account not found", consts.ErrNotFound) + } + return uuid.Nil, fmt.Errorf("failed to scan rows: %w", err) + } + + // create the mock shop + + shopID = uuid.New() + + _, err = tx.Exec( + ctx, + fmt.Sprintf( + ` + INSERT INTO + %s ( + user_id, + account_id, + shop_id, + name + ) + VALUES ( + @user_id, + @account_id, + @shop_id, + @name + ) + `, + shopTableName, + ), + pgx.NamedArgs{ + "account_id": acctID, + "user_id": userID, + "shop_id": shopID, + "name": name, + }, + ) + if err != nil { + return uuid.Nil, fmt.Errorf("failed to perform query to create mock shop: %w", err) + } + + if err := tx.Commit(ctx); err != nil { + return uuid.Nil, fmt.Errorf("failed to commit transaction: %w", err) + } + + return shopID, nil +} + +func (db *Store) ListMockShopsForPlatform(ctx context.Context, acctID int64, platform Platform) ([]MockShop, error) { + shopTableName, err := getMockShopTableName(platform) + if err != nil { + return nil, err + } + + rows, err := db.db.Query( + ctx, + fmt.Sprintf( + ` + SELECT + shop_id, + name + FROM + %s + WHERE + account_id = @account_id + `, + shopTableName, + ), + pgx.NamedArgs{ + "account_id": acctID, + }, + ) + if err != nil { + return nil, fmt.Errorf("failed to perform query: %w", err) + } + + vs, err := pgx.CollectRows(rows, pgx.RowToStructByNameLax[struct { + Shop_id uuid.UUID + Name string + }]) + if err != nil { + return nil, fmt.Errorf("failed to scan rows: %w", err) + } + + shops := make([]MockShop, len(vs)) + for i, v := range vs { + shops[i] = MockShop{ + AccountShopIDs: AccountShopIDs{ + AccountIDs: AccountIDs{ + AccountID: acctID, + }, + Platform: platform, + ShopID: v.Shop_id.String(), + }, + Name: v.Name, + } + } + + return shops, nil +} + +func (db *Store) GetMockShop(ctx context.Context, acctID int64, platform Platform, shopID string) (*MockShop, error) { + shopTableName, err := getMockShopTableName(platform) + if err != nil { + return nil, err + } + + rows, err := db.db.Query( + ctx, + fmt.Sprintf( + ` + SELECT + name + FROM + %s + WHERE + account_id = @account_id + AND shop_id = @shop_id + `, + shopTableName, + ), + pgx.NamedArgs{ + "account_id": acctID, + "shop_id": shopID, + }, + ) + if err != nil { + return nil, fmt.Errorf("failed to perform query: %w", err) + } + + name, err := pgx.CollectExactlyOneRow(rows, pgx.RowTo[string]) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return nil, consts.ErrNotFound + } + return nil, fmt.Errorf("failed to scan rows: %w", err) + } + + return &MockShop{ + AccountShopIDs: AccountShopIDs{ + AccountIDs: AccountIDs{ + AccountID: acctID, + }, + Platform: platform, + ShopID: shopID, + }, + Name: name, + }, nil +} + +func getMockShopTableName(platform Platform) (string, error) { + switch platform { + case Amazon: + return "mock.shop_amazon", nil + case BigCartel: + return "mock.shop_big_cartel", nil + case Ebay: + return "mock.shop_ebay", nil + case Ecwid: + return "mock.shop_ecwid", nil + case Etsy: + return "mock.shop_etsy", nil + case Shopify: + return "mock.shop_shopify", nil + case SquareOnline: + return "mock.shop_square_online", nil + case Squarespace: + return "mock.shop_squarespace", nil + case Tiktok: + return "mock.shop_tiktok", nil + case WalmartMarketplace: + return "mock.shop_walmart_marketplace", nil + case Wix: + return "mock.shop_wix", nil + case WooCommerce: + return "mock.shop_woo_commerce", nil + case Zoho: + return "mock.shop_zoho", nil + default: + return "", fmt.Errorf("%w: unrecognized platform", consts.ErrBadRequest) + } +} + // additional context func (db *Store) GetAccountPointerByUserID(ctx context.Context, userID string) (*Account, error) { diff --git a/internal/domains/accounts/store_with_context.go b/internal/domains/accounts/store_with_context.go index e48abc7..50b5d55 100644 --- a/internal/domains/accounts/store_with_context.go +++ b/internal/domains/accounts/store_with_context.go @@ -5,6 +5,7 @@ package accounts import ( "context" + "github.com/google/uuid" ) type StoreWithContext struct { @@ -47,6 +48,18 @@ func (v_ctx *StoreWithContext) GetListingsForShop(acctID int64, platform Platfor return v_ctx.Store.GetListingsForShop(v_ctx.ctx, acctID, platform, shopID) } +func (v_ctx *StoreWithContext) CreateMockShop(acctID int64, platform Platform, name string) (uuid.UUID, error) { + return v_ctx.Store.CreateMockShop(v_ctx.ctx, acctID, platform, name) +} + +func (v_ctx *StoreWithContext) ListMockShopsForPlatform(acctID int64, platform Platform) ([]MockShop, error) { + return v_ctx.Store.ListMockShopsForPlatform(v_ctx.ctx, acctID, platform) +} + +func (v_ctx *StoreWithContext) GetMockShop(acctID int64, platform Platform, shopID string) (*MockShop, error) { + return v_ctx.Store.GetMockShop(v_ctx.ctx, acctID, platform, shopID) +} + func (v_ctx *StoreWithContext) GetAccountPointerByUserID(userID string) (*Account, error) { return v_ctx.Store.GetAccountPointerByUserID(v_ctx.ctx, userID) } diff --git a/internal/server/api/accounts/router.go b/internal/server/api/accounts/router.go index 1241b65..579ce53 100644 --- a/internal/server/api/accounts/router.go +++ b/internal/server/api/accounts/router.go @@ -36,7 +36,10 @@ func Routes( } r.POST("", response.Handler(as.createAccount)) - r.PUT("/:acctID/platforms/:platform/order-index", pub.Publish("/:acctID/platforms"), response.Handler(as.setOrderOfPlatformOnAccountPage)) + + platformGroup := r.Group("/:acctID/platforms/:platform", pub.Publish("/:acctID/platforms")) + platformGroup.PUT("/order-index", response.Handler(as.setOrderOfPlatformOnAccountPage)) + platformGroup.POST("/shops/mocks", response.Handler(as.createMockShop)) syncGroups := r.Group("/:acctID/inventory/sync-groups") syncGroups.POST("", pub.Publish("/:acctID/inventory/sync-groups"), response.Handler(as.saveNewSyncGroup)) @@ -251,6 +254,53 @@ func (s *accountSubrouter) setOrderOfPlatformOnAccountPage(c *gin.Context) (resp return response.StatusNoContent(), nil } +// POST /:acctID/platforms/:platform/shops/mocks +func (s *accountSubrouter) createMockShop(c *gin.Context) (response.Response, error) { + acctID := auth.GetIdentity(c).Account.AccountID + + // validate parameters + + var platform accounts.Platform + if v := c.Param("platform"); v == "" { + return nil, response.BadRequest(). + Msg("no platform provided") + } else if p, err := accounts.NewPlatform(v); err != nil { + return nil, response.BadRequest(). + Wrap(err). + Msgf("unrecognized platform: %v", v) + } else { + platform = p + } + + var name string + if v, ok := c.GetPostForm("name"); !ok || v == "" { + return nil, response.BadRequest(). + Msg("no order-index provided") + } else { + name = v + } + + s.log.Warnf("not implemented: mock store created: account_id = %d; platform = %s; name = %s", acctID, platform, name) + + // create the mock account + + id, err := s.accts.CreateMockShop(c, acctID, platform, name) + if err != nil { + return nil, fmt.Errorf("failed to save record: %w", err) + } + + location := fmt.Sprintf("/ui/accounts/%d/platforms/%s/mock-shops/%s", acctID, lowerSnakeCase(platform), id) + + var res response.Response + if c.GetHeader("HX-Request") == "true" { + res = response.StatusCreated() + } else { + res = response.SeeOther(location) + } + + return res.HXLocation(location), nil +} + func lowerSnakeCase(s accounts.Platform) string { return strings.ToLower(strings.Join(strings.Split(string(s), " "), "_")) } diff --git a/internal/server/ui/router.go b/internal/server/ui/router.go index 39e91bf..a9eeb97 100644 --- a/internal/server/ui/router.go +++ b/internal/server/ui/router.go @@ -69,6 +69,21 @@ func Routes( "lowerSnakeCase": func(s string) string { return strings.ToLower(strings.Join(strings.Split(s, " "), "_")) }, + "splitSnakeCase": func(s string) string { + return strings.Join(strings.Split(s, "_"), " ") + }, + "capitalize": func(s string) string { + ss := strings.Split(s, " ") + us := make([]string, len(ss)) + for i, v := range ss { + if len(v) == 0 { + us[i] = v + } else { + us[i] = strings.ToUpper(v[:1]) + v[1:] + } + } + return strings.Join(us, " ") + }, // arithmetic "addInt": func(a, b int) int { diff --git a/styles/index.css b/styles/index.css index 1e17af4..643596d 100644 --- a/styles/index.css +++ b/styles/index.css @@ -7,7 +7,6 @@ "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --color-sky-400: oklch(74.6% 0.16 232.661); --color-black: #000; --color-white: #fff; --text-sm: 0.875rem; @@ -16,15 +15,14 @@ --text-lg--line-height: calc(1.75 / 1.125); --text-xl: 1.25rem; --text-xl--line-height: calc(1.75 / 1.25); + --text-2xl: 1.5rem; + --text-2xl--line-height: calc(2 / 1.5); --text-3xl: 1.875rem; --text-6xl: 3.75rem; --font-weight-semibold: 600; --font-weight-bold: 700; --radius-lg: var(--radius); - --ease-in: cubic-bezier(0.4, 0, 1, 1); - --animate-ping: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite; --animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; - --animate-bounce: bounce 1s infinite; --default-transition-duration: 150ms; --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); --default-font-family: var(--font-sans); @@ -218,6 +216,9 @@ .m-\[0\.5em\] { margin: 0.5em; } + .m-\[0\.25em\] { + margin: 0.25em; + } .m-\[1em\] { margin: 1em; } @@ -233,8 +234,11 @@ .my-\[1rem\] { margin-block: 1rem; } - .my-\[2rem\] { - margin-block: 2rem; + .my-\[2em\] { + margin-block: 2em; + } + .my-\[5em\] { + margin-block: 5em; } .mt-\[0\.25em\] { margin-top: 0.25em; @@ -245,27 +249,21 @@ .mt-\[1em\] { margin-top: 1em; } - .mt-\[2em\] { - margin-top: 2em; - } .mt-\[3em\] { margin-top: 3em; } + .mt-\[5em\] { + margin-top: 5em; + } .mb-\[0\.5em\] { margin-bottom: 0.5em; } - .mb-\[0\.5rem\] { - margin-bottom: 0.5rem; - } .mb-\[1\.25rem\] { margin-bottom: 1.25rem; } .mb-\[1em\] { margin-bottom: 1em; } - .mb-\[1rem\] { - margin-bottom: 1rem; - } .mb-\[3em\] { margin-bottom: 3em; } @@ -287,21 +285,12 @@ .hidden { display: none; } - .inline { - display: inline; - } .inline-block { display: inline-block; } .table { display: table; } - .h-\[1em\] { - height: 1em; - } - .h-\[2em\] { - height: 2em; - } .h-\[2rem\] { height: 2rem; } @@ -317,21 +306,12 @@ .w-full { width: 100%; } - .max-w-\[30vw\] { - max-width: 30vw; - } .max-w-\[50\%\] { max-width: 50%; } .max-w-\[80vw\] { max-width: 80vw; } - .max-w-\[100px\] { - max-width: 100px; - } - .max-w-\[200px\] { - max-width: 200px; - } .max-w-full { max-width: 100%; } @@ -356,63 +336,30 @@ .basis-full { flex-basis: 100%; } - .transform { - transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,); - } - .animate-bounce { - animation: var(--animate-bounce); - } - .animate-ping { - animation: var(--animate-ping); - } .animate-pulse { animation: var(--animate-pulse); } .cursor-default { cursor: default; } - .cursor-none { - cursor: none; - } .cursor-pointer { cursor: pointer; } .resize { resize: both; } + .list-disc { + list-style-type: disc; + } .list-none { list-style-type: none; } - .grid-cols-\[1fr_1fr_1fr\] { - grid-template-columns: 1fr 1fr 1fr; - } - .grid-cols-\[1fr_2fr_1fr\] { - grid-template-columns: 1fr 2fr 1fr; - } - .grid-cols-\[1fr_10fr_1fr\] { - grid-template-columns: 1fr 10fr 1fr; - } - .grid-cols-\[1fr_max-content-1fr\] { - grid-template-columns: 1fr max-content-1fr; - } - .grid-cols-\[1fr_max-content\] { - grid-template-columns: 1fr max-content; - } - .grid-cols-\[1fr_max-content_1fr\] { - grid-template-columns: 1fr max-content 1fr; - } - .grid-cols-\[1fr_max-content_calc\(1fr\+1px\)\] { - grid-template-columns: 1fr max-content calc(1fr + 1px); - } .grid-cols-\[2fr_8fr_2fr\] { grid-template-columns: 2fr 8fr 2fr; } .grid-cols-\[6rem_10fr_6rem\] { grid-template-columns: 6rem 10fr 6rem; } - .grid-cols-\[calc\(1fr\+1px\)_max-content_calc\(1fr\+1px\)\] { - grid-template-columns: calc(1fr + 1px) max-content calc(1fr + 1px); - } .grid-cols-\[max-content_max-content\] { grid-template-columns: max-content max-content; } @@ -434,15 +381,9 @@ .justify-start { justify-content: flex-start; } - .gap-\[0\.5em\] { - gap: 0.5em; - } .gap-\[0\.25em\] { gap: 0.25em; } - .gap-\[1e\] { - gap: 1e; - } .gap-\[1em\] { gap: 1em; } @@ -470,20 +411,27 @@ .rounded-lg { border-radius: var(--radius); } + .rounded-md { + border-radius: calc(var(--radius) - 2px); + } .rounded-sm { border-radius: calc(var(--radius) - 4px); } - .border-\[1em\] { + .border { border-style: var(--tw-border-style); - border-width: 1em; + border-width: 1px; + } + .border-\[0\.5px\] { + border-style: var(--tw-border-style); + border-width: 0.5px; } .border-\[1px\] { border-style: var(--tw-border-style); border-width: 1px; } - .border-b-\[1px\] { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 1px; + .border-\[10px\] { + border-style: var(--tw-border-style); + border-width: 10px; } .border-b-\[2px\] { border-bottom-style: var(--tw-border-style); @@ -497,14 +445,11 @@ --tw-border-style: solid; border-style: solid; } - .border-accent { - border-color: var(--accent); + .border-background { + border-color: var(--background); } - .border-card { - border-color: var(--card); - } - .border-card-foreground { - border-color: var(--card-foreground); + .border-foreground { + border-color: var(--foreground); } .border-input { border-color: var(--input); @@ -512,11 +457,8 @@ .border-sidebar-border { border-color: var(--sidebar-border); } - .border-b-\[1px_solid\] { - border-bottom-color: 1px solid; - } - .border-b-\[1px_solid_black\] { - border-bottom-color: 1px solid black; + .border-white { + border-color: var(--color-white); } .bg-accent { background-color: var(--accent); @@ -527,48 +469,21 @@ .bg-card { background-color: var(--card); } - .bg-foreground { - background-color: var(--foreground); - } - .bg-input { - background-color: var(--input); - } - .bg-muted { - background-color: var(--muted); - } - .bg-primary { - background-color: var(--primary); - } - .bg-secondary { - background-color: var(--secondary); - } .p-\[0\.5em\] { padding: 0.5em; } + .p-\[0\.25em\] { + padding: 0.25em; + } .p-\[1em\] { padding: 1em; } - .p-\[1rem\] { - padding: 1rem; - } - .px-\[0\.5em\] { - padding-inline: 0.5em; - } - .px-\[0\.75em\] { - padding-inline: 0.75em; - } .px-\[2em\] { padding-inline: 2em; } .py-\[0\.5em\] { padding-block: 0.5em; } - .py-\[1em\] { - padding-block: 1em; - } - .py-\[1rem\] { - padding-block: 1rem; - } .pt-\[1em\] { padding-top: 1em; } @@ -590,6 +505,10 @@ .font-display { font-family: var(--display-family); } + .text-2xl { + font-size: var(--text-2xl); + line-height: var(--tw-leading, var(--text-2xl--line-height)); + } .text-lg { font-size: var(--text-lg); line-height: var(--tw-leading, var(--text-lg--line-height)); @@ -613,6 +532,9 @@ .text-nowrap { text-wrap: nowrap; } + .capitalize { + text-transform: capitalize; + } .italic { font-style: italic; } @@ -627,115 +549,22 @@ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); transition-duration: var(--tw-duration, var(--default-transition-duration)); } - .transition-\[left\] { - transition-property: left; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } .transition-\[scale\] { transition-property: scale; transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); transition-duration: var(--tw-duration, var(--default-transition-duration)); } - .transition-\[top\] { - transition-property: top; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - .transition-\[y\] { - transition-property: y; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - .transition-all { - transition-property: all; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - .transition-opacity { - transition-property: opacity; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - .transition-shadow { - transition-property: box-shadow; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - .delay-150 { - transition-delay: 150ms; - } - .delay-1000 { - transition-delay: 1000ms; - } - .delay-3000 { - transition-delay: 3000ms; - } - .delay-5000 { - transition-delay: 5000ms; - } - .delay-\[3s\] { - transition-delay: 3s; - } - .delay-\[3000\] { - transition-delay: 3000; - } - .duration-500 { - --tw-duration: 500ms; - transition-duration: 500ms; - } - .duration-3000 { - --tw-duration: 3000ms; - transition-duration: 3000ms; - } - .ease-in { - --tw-ease: var(--ease-in); - transition-timing-function: var(--ease-in); - } - .group-open\:border-b-\[0\.5rem\] { - &:is(:where(.group):is([open], :popover-open, :open) *) { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 0.5rem; - } - } .group-open\:border-b-\[0\.25rem\] { &:is(:where(.group):is([open], :popover-open, :open) *) { border-bottom-style: var(--tw-border-style); border-bottom-width: 0.25rem; } } - .group-open\:border-b-\[1px\] { - &:is(:where(.group):is([open], :popover-open, :open) *) { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 1px; - } - } - .group-open\:border-b-\[1rem\] { - &:is(:where(.group):is([open], :popover-open, :open) *) { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 1rem; - } - } - .group-open\:border-b-accent { - &:is(:where(.group):is([open], :popover-open, :open) *) { - border-bottom-color: var(--accent); - } - } .group-open\:border-b-background { &:is(:where(.group):is([open], :popover-open, :open) *) { border-bottom-color: var(--background); } } - .group-open\:border-b-black { - &:is(:where(.group):is([open], :popover-open, :open) *) { - border-bottom-color: var(--color-black); - } - } - .group-open\:bg-background { - &:is(:where(.group):is([open], :popover-open, :open) *) { - background-color: var(--background); - } - } .group-hover\:scale-120 { &:is(:where(.group):hover *) { @media (hover: hover) { @@ -746,127 +575,16 @@ } } } - .marker\:inline { - & *::marker { - display: inline; - } - &::marker { - display: inline; - } - & *::-webkit-details-marker { - display: inline; - } - &::-webkit-details-marker { - display: inline; - } - } - .marker\:inline-block { - & *::marker { - display: inline-block; - } - &::marker { - display: inline-block; - } - & *::-webkit-details-marker { - display: inline-block; - } - &::-webkit-details-marker { - display: inline-block; - } - } - .marker\:list-none { - & *::marker { - list-style-type: none; - } - &::marker { - list-style-type: none; - } - & *::-webkit-details-marker { - list-style-type: none; - } - &::-webkit-details-marker { - list-style-type: none; - } - } - .marker\:bg-accent { - & *::marker { - background-color: var(--accent); - } - &::marker { - background-color: var(--accent); - } - & *::-webkit-details-marker { - background-color: var(--accent); - } - &::-webkit-details-marker { - background-color: var(--accent); - } - } - .marker\:text-sky-400 { - & *::marker { - color: var(--color-sky-400); - } - &::marker { - color: var(--color-sky-400); - } - & *::-webkit-details-marker { - color: var(--color-sky-400); - } - &::-webkit-details-marker { - color: var(--color-sky-400); - } - } - .details-content\:transform-\[translate\(0px\,-1em\)\] { - &::details-content { - transform: translate(0px,-1em); - } - } .details-content\:transform-\[translate\(0px\,-2em\)\] { &::details-content { transform: translate(0px,-2em); } } - .details-content\:transform-\[translate\(0px\,-3em\)\] { - &::details-content { - transform: translate(0px,-3em); - } - } - .details-content\:transform-\[translate\(0px\,-100px\)\] { - &::details-content { - transform: translate(0px,-100px); - } - } - .details-content\:transform-\[translate\(0px\,100px\)\] { - &::details-content { - transform: translate(0px,100px); - } - } - .details-content\:transform-\[translate\(100px\)\] { - &::details-content { - transform: translate(100px); - } - } - .details-content\:transform-\[translatey\(0px\,100px\)\] { - &::details-content { - transform: translatey(0px,100px); - } - } - .details-content\:border-none { - &::details-content { - --tw-border-style: none; - border-style: none; - } - } .details-content\:border-transparent { &::details-content { border-color: transparent; } } - .details-content\:opacity-\[0\%\] { - &::details-content { - opacity: 0%; - } - } .details-content\:opacity-\[0\] { &::details-content { opacity: 0; @@ -877,20 +595,6 @@ outline-color: transparent; } } - .details-content\:transition { - &::details-content { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - } - .details-content\:transition-\[opacity\] { - &::details-content { - transition-property: opacity; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - } .details-content\:transition-\[opacity_transform\] { &::details-content { transition-property: opacity transform; @@ -898,50 +602,18 @@ transition-duration: var(--tw-duration, var(--default-transition-duration)); } } - .details-content\:transition-opacity { - &::details-content { - transition-property: opacity; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - } - .details-content\:transition-transform { - &::details-content { - transition-property: transform, translate, scale, rotate; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - } .after\:float-right { &::after { content: var(--tw-content); float: right; } } - .after\:ml-\[1em\] { - &::after { - content: var(--tw-content); - margin-left: 1em; - } - } - .after\:text-\[1em\] { - &::after { - content: var(--tw-content); - font-size: 1em; - } - } .after\:text-\[2em\] { &::after { content: var(--tw-content); font-size: 2em; } } - .after\:text-\[3em\] { - &::after { - content: var(--tw-content); - font-size: 3em; - } - } .after\:leading-\[1em\] { &::after { content: var(--tw-content); @@ -949,27 +621,6 @@ line-height: 1em; } } - .after\:leading-\[2em\] { - &::after { - content: var(--tw-content); - --tw-leading: 2em; - line-height: 2em; - } - } - .after\:font-\[2em\] { - &::after { - content: var(--tw-content); - --tw-font-weight: 2em; - font-weight: 2em; - } - } - .after\:font-\[3em\] { - &::after { - content: var(--tw-content); - --tw-font-weight: 3em; - font-weight: 3em; - } - } .after\:font-bold { &::after { content: var(--tw-content); @@ -977,68 +628,12 @@ font-weight: var(--font-weight-bold); } } - .after\:content-\[\'\&\#9654\'\] { - &::after { - --tw-content: '▶'; - content: var(--tw-content); - } - } - .after\:content-\[\'\&\#9655\'\] { - &::after { - --tw-content: '▷'; - content: var(--tw-content); - } - } - .after\:content-\[\'\&\#9656\'\] { - &::after { - --tw-content: '▸'; - content: var(--tw-content); - } - } - .after\:content-\[\'\&\#x25B6\'\] { - &::after { - --tw-content: '▶'; - content: var(--tw-content); - } - } .after\:content-\[\'\+\'\] { &::after { --tw-content: '+'; content: var(--tw-content); } } - .after\:content-\[\'\<\'\] { - &::after { - --tw-content: '<'; - content: var(--tw-content); - } - } - .after\:content-\[\'\>\'\] { - &::after { - --tw-content: '>'; - content: var(--tw-content); - } - } - .after\:content-\[\'▷\'\] { - &::after { - --tw-content: '▷'; - content: var(--tw-content); - } - } - .after\:content-\[\>\] { - &::after { - --tw-content: >; - content: var(--tw-content); - } - } - .group-open\:after\:content-\[\'\+\'\] { - &:is(:where(.group):is([open], :popover-open, :open) *) { - &::after { - --tw-content: '+'; - content: var(--tw-content); - } - } - } .group-open\:after\:content-\[\'-\'\] { &:is(:where(.group):is([open], :popover-open, :open) *) { &::after { @@ -1047,14 +642,6 @@ } } } - .group-open\:after\:content-\[\'v\'\] { - &:is(:where(.group):is([open], :popover-open, :open) *) { - &::after { - --tw-content: 'v'; - content: var(--tw-content); - } - } - } .group-open\/create-mock-shop\:after\:content-\[\'-\'\] { &:is(:where(.group\/create-mock-shop):is([open], :popover-open, :open) *) { &::after { @@ -1063,14 +650,6 @@ } } } - .group-open\/create-mock-store\:after\:content-\[\'-\'\] { - &:is(:where(.group\/create-mock-store):is([open], :popover-open, :open) *) { - &::after { - --tw-content: '-'; - content: var(--tw-content); - } - } - } .group-open\/link-shops\:after\:content-\[\'-\'\] { &:is(:where(.group\/link-shops):is([open], :popover-open, :open) *) { &::after { @@ -1079,6 +658,11 @@ } } } + .open\:mb-\[1em\] { + &:is([open], :popover-open, :open) { + margin-bottom: 1em; + } + } .open\:mb-\[2em\] { &:is([open], :popover-open, :open) { margin-bottom: 2em; @@ -1089,35 +673,30 @@ display: grid; } } - .open\:bg-background { + .open\:border-b-\[1px\] { &:is([open], :popover-open, :open) { - background-color: var(--background); + border-bottom-style: var(--tw-border-style); + border-bottom-width: 1px; } } - .open\:transition-opacity { + .open\:border-b-accent { &:is([open], :popover-open, :open) { - transition-property: opacity; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); + border-bottom-color: var(--accent); } } - .open\:duration-3000 { + .open\:border-b-background { &:is([open], :popover-open, :open) { - --tw-duration: 3000ms; - transition-duration: 3000ms; + border-bottom-color: var(--background); } } - .open\:ease-in { + .open\:pb-\[1em\] { &:is([open], :popover-open, :open) { - --tw-ease: var(--ease-in); - transition-timing-function: var(--ease-in); + padding-bottom: 1em; } } - .open\:details-content\:transform-\[translate\(0px\)\] { + .open\:pb-\[2em\] { &:is([open], :popover-open, :open) { - &::details-content { - transform: translate(0px); - } + padding-bottom: 2em; } } .open\:details-content\:transform-\[translate\(0px\,0px\)\] { @@ -1127,14 +706,6 @@ } } } - .open\:details-content\:border-x-0 { - &:is([open], :popover-open, :open) { - &::details-content { - border-inline-style: var(--tw-border-style); - border-inline-width: 0px; - } - } - } .open\:details-content\:border-x-\[0px\] { &:is([open], :popover-open, :open) { &::details-content { @@ -1143,22 +714,6 @@ } } } - .open\:details-content\:border-t { - &:is([open], :popover-open, :open) { - &::details-content { - border-top-style: var(--tw-border-style); - border-top-width: 1px; - } - } - } - .open\:details-content\:border-t-\[0\.2rem\] { - &:is([open], :popover-open, :open) { - &::details-content { - border-top-style: var(--tw-border-style); - border-top-width: 0.2rem; - } - } - } .open\:details-content\:border-t-\[0\.25rem\] { &:is([open], :popover-open, :open) { &::details-content { @@ -1167,22 +722,6 @@ } } } - .open\:details-content\:border-t-\[0\.125rem\] { - &:is([open], :popover-open, :open) { - &::details-content { - border-top-style: var(--tw-border-style); - border-top-width: 0.125rem; - } - } - } - .open\:details-content\:border-t-\[0\.175rem\] { - &:is([open], :popover-open, :open) { - &::details-content { - border-top-style: var(--tw-border-style); - border-top-width: 0.175rem; - } - } - } .open\:details-content\:border-t-\[0\.225rem\] { &:is([open], :popover-open, :open) { &::details-content { @@ -1191,30 +730,6 @@ } } } - .open\:details-content\:border-r-0 { - &:is([open], :popover-open, :open) { - &::details-content { - border-right-style: var(--tw-border-style); - border-right-width: 0px; - } - } - } - .open\:details-content\:border-b-0 { - &:is([open], :popover-open, :open) { - &::details-content { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 0px; - } - } - } - .open\:details-content\:border-b-\[0\.25rem\] { - &:is([open], :popover-open, :open) { - &::details-content { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 0.25rem; - } - } - } .open\:details-content\:border-b-\[0px\] { &:is([open], :popover-open, :open) { &::details-content { @@ -1223,14 +738,6 @@ } } } - .open\:details-content\:border-l-0 { - &:is([open], :popover-open, :open) { - &::details-content { - border-left-style: var(--tw-border-style); - border-left-width: 0px; - } - } - } .open\:details-content\:border-dotted { &:is([open], :popover-open, :open) { &::details-content { @@ -1239,14 +746,6 @@ } } } - .open\:details-content\:border-solid { - &:is([open], :popover-open, :open) { - &::details-content { - --tw-border-style: solid; - border-style: solid; - } - } - } .open\:details-content\:border-x-transparent { &:is([open], :popover-open, :open) { &::details-content { @@ -1261,13 +760,6 @@ } } } - .open\:details-content\:border-b-background { - &:is([open], :popover-open, :open) { - &::details-content { - border-bottom-color: var(--background); - } - } - } .open\:details-content\:border-b-transparent { &:is([open], :popover-open, :open) { &::details-content { @@ -1282,66 +774,6 @@ } } } - .open\:details-content\:outline-transparent { - &:is([open], :popover-open, :open) { - &::details-content { - outline-color: transparent; - } - } - } - .open\:details-content\:outline-dotted { - &:is([open], :popover-open, :open) { - &::details-content { - --tw-outline-style: dotted; - outline-style: dotted; - } - } - } - .open\:after\:content-\[\'-\'\] { - &:is([open], :popover-open, :open) { - &::after { - --tw-content: '-'; - content: var(--tw-content); - } - } - } - .hover\:scale-110 { - &:hover { - @media (hover: hover) { - --tw-scale-x: 110%; - --tw-scale-y: 110%; - --tw-scale-z: 110%; - scale: var(--tw-scale-x) var(--tw-scale-y); - } - } - } - .hover\:scale-120 { - &:hover { - @media (hover: hover) { - --tw-scale-x: 120%; - --tw-scale-y: 120%; - --tw-scale-z: 120%; - scale: var(--tw-scale-x) var(--tw-scale-y); - } - } - } - .hover\:scale-150 { - &:hover { - @media (hover: hover) { - --tw-scale-x: 150%; - --tw-scale-y: 150%; - --tw-scale-z: 150%; - scale: var(--tw-scale-x) var(--tw-scale-y); - } - } - } - .hover\:cursor-pointer { - &:hover { - @media (hover: hover) { - cursor: pointer; - } - } - } .hover\:bg-accent { &:hover { @media (hover: hover) { @@ -1387,17 +819,6 @@ filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); } } - .dark\:invert-\[100\%\] { - @media (prefers-color-scheme: dark) { - --tw-invert: invert(100%); - filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); - } - } - .\[open\]\:bg-background { - &:is(open) { - background-color: var(--background); - } - } } @layer base { select { @@ -1710,26 +1131,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 { syntax: "*"; inherits: false; @@ -1792,14 +1193,6 @@ syntax: "*"; inherits: false; } -@property --tw-duration { - syntax: "*"; - inherits: false; -} -@property --tw-ease { - syntax: "*"; - inherits: false; -} @property --tw-scale-x { syntax: "*"; inherits: false; @@ -1815,6 +1208,26 @@ inherits: false; 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 { syntax: "*"; initial-value: ""; @@ -1824,35 +1237,14 @@ syntax: "*"; inherits: false; } -@keyframes ping { - 75%, 100% { - transform: scale(2); - opacity: 0; - } -} @keyframes pulse { 50% { opacity: 0.5; } } -@keyframes bounce { - 0%, 100% { - transform: translateY(-25%); - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - } - 50% { - transform: none; - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - } -} @layer properties { @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 { - --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-font-weight: initial; --tw-blur: initial; @@ -1868,11 +1260,14 @@ --tw-drop-shadow-color: initial; --tw-drop-shadow-alpha: 100%; --tw-drop-shadow-size: initial; - --tw-duration: initial; - --tw-ease: initial; --tw-scale-x: 1; --tw-scale-y: 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-leading: initial; } diff --git a/templates/components/accounts/{acctID.int64}/platforms/{platform}/link-section.html.tmpl b/templates/components/accounts/{acctID.int64}/platforms/{platform}/link-section.html.tmpl index 69ae572..b779ec5 100644 --- a/templates/components/accounts/{acctID.int64}/platforms/{platform}/link-section.html.tmpl +++ b/templates/components/accounts/{acctID.int64}/platforms/{platform}/link-section.html.tmpl @@ -1,11 +1,15 @@ {{- $acctID := .PathParams.acctID }} {{- $platform := .PathParams.platform }} +{{- $parsedPlatform := parsePlatform $platform }}
+ + +