mock mode: database queries implemented and checkbox on account page built
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
BEGIN;
|
||||
|
||||
|
||||
DROP TABLE mock_mode;
|
||||
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,10 @@
|
||||
BEGIN;
|
||||
|
||||
|
||||
CREATE TABLE mock_mode (
|
||||
account_id INTEGER PRIMARY KEY REFERENCES accounts,
|
||||
mock_mode BOOLEAN NOT NULL DEFAULT FALSE
|
||||
);
|
||||
|
||||
|
||||
COMMIT;
|
||||
@@ -27,6 +27,72 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func (db *Store) SetMockMode(ctx context.Context, acctID int64, on bool) error {
|
||||
_, err := db.db.Exec(
|
||||
ctx,
|
||||
`
|
||||
INSERT INTO
|
||||
mock_mode (
|
||||
account_id,
|
||||
mock_mode
|
||||
)
|
||||
VALUES (
|
||||
@account_id,
|
||||
@mock_mode
|
||||
)
|
||||
ON CONFLICT (account_id) DO UPDATE
|
||||
SET
|
||||
mock_mode = @mock_mode
|
||||
`,
|
||||
pgx.NamedArgs{
|
||||
"account_id": acctID,
|
||||
"mock_mode": on,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to perform query: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *Store) GetMockMode(ctx context.Context, acctID int64) (on bool, err error) {
|
||||
rows, err := db.db.Query(
|
||||
ctx,
|
||||
`
|
||||
WITH account_query AS (
|
||||
SELECT
|
||||
account_id
|
||||
FROM
|
||||
accounts
|
||||
WHERE
|
||||
account_id = @account_id
|
||||
)
|
||||
SELECT
|
||||
COALESCE(mm.mock_mode, false)
|
||||
FROM
|
||||
account_query AS aq
|
||||
LEFT JOIN
|
||||
mock_mode AS mm
|
||||
USING
|
||||
(account_id)
|
||||
`,
|
||||
pgx.NamedArgs{
|
||||
"account_id": acctID,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to perform query: %w", err)
|
||||
}
|
||||
|
||||
on, err = pgx.CollectExactlyOneRow(rows, pgx.RowTo[bool])
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to scan rows: %w", err)
|
||||
}
|
||||
|
||||
return on, 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 {
|
||||
|
||||
@@ -69,6 +69,10 @@ func (v_ctx *StoreWithContext) StartEditingMockSyncGroup(acctID int64, syncGroup
|
||||
return v_ctx.Store.StartEditingMockSyncGroup(v_ctx.ctx, acctID, syncGroupID)
|
||||
}
|
||||
|
||||
func (v_ctx *StoreWithContext) SaveMockSyncGroupBeingEdited(acctID int64) (int64, error) {
|
||||
return v_ctx.Store.SaveMockSyncGroupBeingEdited(v_ctx.ctx, acctID)
|
||||
}
|
||||
|
||||
func (v_ctx *StoreWithContext) CancelEditingOfMockSyncGroupForAccount(acctID int64) (int64, bool, error) {
|
||||
return v_ctx.Store.CancelEditingOfMockSyncGroupForAccount(v_ctx.ctx, acctID)
|
||||
}
|
||||
@@ -85,10 +89,22 @@ func (v_ctx *StoreWithContext) GetMockSyncGroupEditingListing(acctID int64, sync
|
||||
return v_ctx.Store.GetMockSyncGroupEditingListing(v_ctx.ctx, acctID, syncGroupID, orderIndex)
|
||||
}
|
||||
|
||||
func (v_ctx *StoreWithContext) AddListingToMockSyncGroupBeingEdited(acctID int64) (int64, int, error) {
|
||||
return v_ctx.Store.AddListingToMockSyncGroupBeingEdited(v_ctx.ctx, acctID)
|
||||
}
|
||||
|
||||
func (v_ctx *StoreWithContext) SetShopInListingInMockSyncGroupBeingEdited(acctID int64, syncGroupID int64, orderIndex int, platform Platform, shopID string) error {
|
||||
return v_ctx.Store.SetShopInListingInMockSyncGroupBeingEdited(v_ctx.ctx, acctID, syncGroupID, orderIndex, platform, shopID)
|
||||
}
|
||||
|
||||
func (v_ctx *StoreWithContext) SetListingInListingInMockSyncGroupBeingEdited(acctID int64, syncGroupID int64, orderIndex int, listingID string) error {
|
||||
return v_ctx.Store.SetListingInListingInMockSyncGroupBeingEdited(v_ctx.ctx, acctID, syncGroupID, orderIndex, listingID)
|
||||
}
|
||||
|
||||
func (v_ctx *StoreWithContext) DeleteListingInListingInMockSyncGroupBeingEdited(acctID int64, orderIndex int) error {
|
||||
return v_ctx.Store.DeleteListingInListingInMockSyncGroupBeingEdited(v_ctx.ctx, acctID, orderIndex)
|
||||
}
|
||||
|
||||
func (v_ctx *StoreWithContext) GetAccountPointerByUserID(userID string) (*Account, error) {
|
||||
return v_ctx.Store.GetAccountPointerByUserID(v_ctx.ctx, userID)
|
||||
}
|
||||
@@ -97,6 +113,18 @@ func (v_ctx *StoreWithContext) beginReadonlyTxn(cb func(pgx.Tx) error) error {
|
||||
return v_ctx.Store.beginReadonlyTxn(v_ctx.ctx, cb)
|
||||
}
|
||||
|
||||
func (v_ctx *StoreWithContext) beginTxn(cb func(pgx.Tx) error) error {
|
||||
return v_ctx.Store.beginTxn(v_ctx.ctx, cb)
|
||||
}
|
||||
|
||||
func (v_ctx *StoreWithContext) SetMockMode(acctID int64, on bool) error {
|
||||
return v_ctx.Store.SetMockMode(v_ctx.ctx, acctID, on)
|
||||
}
|
||||
|
||||
func (v_ctx *StoreWithContext) GetMockMode(acctID int64) (bool, error) {
|
||||
return v_ctx.Store.GetMockMode(v_ctx.ctx, acctID)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ func Routes(
|
||||
|
||||
r.POST("", response.Handler(as.createAccount))
|
||||
|
||||
// TODO: make the subroutes more readable
|
||||
|
||||
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))
|
||||
@@ -55,6 +57,9 @@ func Routes(
|
||||
draftListings.PUT("/:orderIndex/listing", response.Handler(as.setListingInSyncGroupListingDraft))
|
||||
draftListings.DELETE("/:orderIndex", response.Handler(as.deleteSyncGroupListingDraft))
|
||||
|
||||
mockModeGroup := r.Group("/:acctID/mock-mode", pub.Publish("/:acctID/mock-mode"))
|
||||
mockModeGroup.PUT("", response.Handler(as.setMockMode))
|
||||
|
||||
mockSyncGroups := syncGroups.Group("/mock")
|
||||
mockSyncGroups.POST("", pub.Publish("/:acctID/inventory/sync-groups/mock"), response.Handler(as.saveNewMockSyncGroup))
|
||||
mockSyncGroups.DELETE("/:syncGroupID", pub.Publish("/:acctID/inventory/sync-groups/mock"), response.Handler(as.deleteMockSyncGroup))
|
||||
@@ -500,6 +505,29 @@ func (s *accountSubrouter) deleteMockSyncGroupListingDraft(c *gin.Context) (resp
|
||||
return response.StatusOK(), nil
|
||||
}
|
||||
|
||||
// PUT /:acctID/mock-mode
|
||||
func (s *accountSubrouter) setMockMode(c *gin.Context) (response.Response, error) {
|
||||
r := c.Request
|
||||
ctx := r.Context()
|
||||
acctID := auth.GetIdentity(ctx).Account.AccountID
|
||||
|
||||
var (
|
||||
on bool
|
||||
)
|
||||
|
||||
err := param.Form("on", param.Bool(&on)).
|
||||
Unmarshal(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.accts.SetMockMode(ctx, acctID, on); err != nil {
|
||||
return nil, response.Errorf("failed to set mock mode: %w", response.ErrorFromConstant(err))
|
||||
}
|
||||
|
||||
return response.StatusOK(), nil
|
||||
}
|
||||
|
||||
// POST /:acctID/inventory/sync-groups/mock
|
||||
func (s *accountSubrouter) saveNewMockSyncGroup(c *gin.Context) (response.Response, error) {
|
||||
r := c.Request
|
||||
|
||||
@@ -21,6 +21,10 @@ func Int64(dst *int64) encoding.TextUnmarshaler {
|
||||
return (*int64Text)(dst)
|
||||
}
|
||||
|
||||
func Bool(dst *bool) encoding.TextUnmarshaler {
|
||||
return (*boolText)(dst)
|
||||
}
|
||||
|
||||
func Platform(dst *accounts.Platform) encoding.TextUnmarshaler {
|
||||
return (*platformText)(dst)
|
||||
}
|
||||
@@ -29,6 +33,7 @@ type (
|
||||
rawText string
|
||||
intText int
|
||||
int64Text int64
|
||||
boolText bool
|
||||
platformText accounts.Platform
|
||||
)
|
||||
|
||||
@@ -55,6 +60,15 @@ func (n *int64Text) UnmarshalText(text []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *boolText) UnmarshalText(text []byte) error {
|
||||
v, err := strconv.ParseBool(string(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*b = boolText(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *platformText) UnmarshalText(text []byte) error {
|
||||
v, err := accounts.NewPlatform(string(text))
|
||||
if err != nil {
|
||||
|
||||
@@ -167,10 +167,22 @@ func ErrorFromConstant(err error) error {
|
||||
cerr := err
|
||||
for cerr != nil {
|
||||
switch cerr {
|
||||
// if the error already is an ErrorResponse with the appropriate status, nothing needs to be done.
|
||||
// else the error needs to be wrapped in the appropriate ErrorResponse.
|
||||
case consts.ErrNotFound:
|
||||
return NotFound()
|
||||
if er, ok := GetError(err); ok {
|
||||
if s, ok := er.GetStatus(); ok && s == http.StatusNotFound {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return NotFound().Wrap(err)
|
||||
case consts.ErrConflict:
|
||||
return Conflict()
|
||||
if er, ok := GetError(err); ok {
|
||||
if s, ok := er.GetStatus(); ok && s == http.StatusConflict {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return Conflict().Wrap(err)
|
||||
}
|
||||
|
||||
uerr, ok := cerr.(interface {
|
||||
|
||||
+83
-25
@@ -197,6 +197,29 @@
|
||||
.bottom-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\] {
|
||||
&:is(table) {
|
||||
display: grid;
|
||||
@@ -322,6 +345,9 @@
|
||||
.table {
|
||||
display: table;
|
||||
}
|
||||
.h-\[1em\] {
|
||||
height: 1em;
|
||||
}
|
||||
.h-\[2rem\] {
|
||||
height: 2rem;
|
||||
}
|
||||
@@ -340,6 +366,9 @@
|
||||
.min-h-full {
|
||||
min-height: 100%;
|
||||
}
|
||||
.w-\[1em\] {
|
||||
width: 1em;
|
||||
}
|
||||
.w-fit {
|
||||
width: fit-content;
|
||||
}
|
||||
@@ -361,6 +390,9 @@
|
||||
.flex-shrink {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
.flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.flex-grow-\[1\] {
|
||||
flex-grow: 1;
|
||||
}
|
||||
@@ -376,6 +408,12 @@
|
||||
.basis-full {
|
||||
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 {
|
||||
animation: var(--animate-pulse);
|
||||
}
|
||||
@@ -536,6 +574,9 @@
|
||||
.font-display {
|
||||
font-family: var(--display-family);
|
||||
}
|
||||
.font-text {
|
||||
font-family: var(--text-family);
|
||||
}
|
||||
.text-lg {
|
||||
font-size: var(--text-lg);
|
||||
line-height: var(--tw-leading, var(--text-lg--line-height));
|
||||
@@ -548,6 +589,13 @@
|
||||
font-size: var(--text-xl);
|
||||
line-height: var(--tw-leading, var(--text-xl--line-height));
|
||||
}
|
||||
.text-\[1\.5em\] {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.font-\[1\.5em\] {
|
||||
--tw-font-weight: 1.5em;
|
||||
font-weight: 1.5em;
|
||||
}
|
||||
.font-bold {
|
||||
--tw-font-weight: var(--font-weight-bold);
|
||||
font-weight: var(--font-weight-bold);
|
||||
@@ -559,6 +607,12 @@
|
||||
.text-nowrap {
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
.text-wrap {
|
||||
text-wrap: wrap;
|
||||
}
|
||||
.text-inherit {
|
||||
color: inherit;
|
||||
}
|
||||
.capitalize {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
@@ -568,6 +622,10 @@
|
||||
.underline {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
.outline {
|
||||
outline-style: var(--tw-outline-style);
|
||||
outline-width: 1px;
|
||||
}
|
||||
.outline-1 {
|
||||
outline-style: var(--tw-outline-style);
|
||||
outline-width: 1px;
|
||||
@@ -1142,6 +1200,26 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
@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;
|
||||
@@ -1224,26 +1302,6 @@
|
||||
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: "";
|
||||
@@ -1261,6 +1319,11 @@
|
||||
@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-outline-style: solid;
|
||||
@@ -1280,11 +1343,6 @@
|
||||
--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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{{- $acctID := .Identity.Account.AccountID }}
|
||||
|
||||
<section
|
||||
class="m-[1rem]"
|
||||
hx-get="/ui/accounts/{{$acctID}}/mock-mode-section"
|
||||
hx-trigger="sse:accounts_{{$acctID}}_mock-mode"
|
||||
hx-swap="morph"
|
||||
>
|
||||
{{- $mockMode := .Accounts.GetMockMode $acctID }}
|
||||
|
||||
<form id="mock-mode-form">
|
||||
<label class="font-display text-[1.5em]">
|
||||
Mock Mode
|
||||
<input
|
||||
type="checkbox"
|
||||
{{- if $mockMode }}
|
||||
checked
|
||||
{{- end }}
|
||||
class="h-[1em] w-[1em]"
|
||||
hx-put="/api/accounts/{{$acctID}}/mock-mode"
|
||||
hx-vals='{"on": {{not $mockMode}}}'
|
||||
hx-swap="none"
|
||||
/>
|
||||
</label>
|
||||
</form>
|
||||
</section>
|
||||
@@ -47,3 +47,5 @@
|
||||
</div>
|
||||
</h2>
|
||||
</section>
|
||||
|
||||
{{ component (printf "accounts/%d/mock-mode-section" $acctID) }}
|
||||
|
||||
Reference in New Issue
Block a user