mock shop page: can delete listings
This commit is contained in:
@@ -365,6 +365,41 @@ func (db *Store) UpdateMockListing(ctx context.Context, listing MockListing) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *Store) DeleteMockListing(ctx context.Context, ids AccountShopListingIDs) error {
|
||||||
|
_, listingTableName, err := getMockShopAndListingsTableNames(ids.Platform)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tag, err := db.db.Exec(
|
||||||
|
ctx,
|
||||||
|
fmt.Sprintf(
|
||||||
|
`
|
||||||
|
DELETE FROM
|
||||||
|
%s
|
||||||
|
WHERE
|
||||||
|
account_id = @account_id
|
||||||
|
AND shop_id = @shop_id
|
||||||
|
AND listing_id = @listing_id
|
||||||
|
`,
|
||||||
|
listingTableName,
|
||||||
|
),
|
||||||
|
pgx.NamedArgs{
|
||||||
|
"account_id": ids.AccountID,
|
||||||
|
"shop_id": ids.ShopID,
|
||||||
|
"listing_id": ids.ListingID,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to perform query: %w", err)
|
||||||
|
}
|
||||||
|
if tag.RowsAffected() == 0 {
|
||||||
|
return fmt.Errorf("%w: listing not found", consts.ErrNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (db *Store) ListMockListingsForShop(ctx context.Context, acctID int64, platform Platform, shopID string) ([]MockListing, error) {
|
func (db *Store) ListMockListingsForShop(ctx context.Context, acctID int64, platform Platform, shopID string) ([]MockListing, error) {
|
||||||
shopTableName, listingsTableName, err := getMockShopAndListingsTableNames(platform)
|
shopTableName, listingsTableName, err := getMockShopAndListingsTableNames(platform)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -72,6 +72,10 @@ func (v_ctx *StoreWithContext) UpdateMockListing(listing MockListing) error {
|
|||||||
return v_ctx.Store.UpdateMockListing(v_ctx.ctx, listing)
|
return v_ctx.Store.UpdateMockListing(v_ctx.ctx, listing)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v_ctx *StoreWithContext) DeleteMockListing(ids AccountShopListingIDs) error {
|
||||||
|
return v_ctx.Store.DeleteMockListing(v_ctx.ctx, ids)
|
||||||
|
}
|
||||||
|
|
||||||
func (v_ctx *StoreWithContext) ListMockListingsForShop(acctID int64, platform Platform, shopID string) ([]MockListing, error) {
|
func (v_ctx *StoreWithContext) ListMockListingsForShop(acctID int64, platform Platform, shopID string) ([]MockListing, error) {
|
||||||
return v_ctx.Store.ListMockListingsForShop(v_ctx.ctx, acctID, platform, shopID)
|
return v_ctx.Store.ListMockListingsForShop(v_ctx.ctx, acctID, platform, shopID)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ func Routes(
|
|||||||
mockShops := platformGroup.Group("/shops/mocks/:shop-id")
|
mockShops := platformGroup.Group("/shops/mocks/:shop-id")
|
||||||
mockShops.POST("/listings", response.Handler(as.addMockListing))
|
mockShops.POST("/listings", response.Handler(as.addMockListing))
|
||||||
mockShops.PUT("/listings/:listing-id", response.Handler(as.updateMockListing))
|
mockShops.PUT("/listings/:listing-id", response.Handler(as.updateMockListing))
|
||||||
|
mockShops.DELETE("/listings/:listing-id", response.Handler(as.deleteMockListing))
|
||||||
|
|
||||||
syncGroups := r.Group("/:acctID/inventory/sync-groups")
|
syncGroups := r.Group("/:acctID/inventory/sync-groups")
|
||||||
syncGroups.POST("", pub.Publish("/:acctID/inventory/sync-groups"), response.Handler(as.saveNewSyncGroup))
|
syncGroups.POST("", pub.Publish("/:acctID/inventory/sync-groups"), response.Handler(as.saveNewSyncGroup))
|
||||||
@@ -344,12 +345,43 @@ func (s *accountSubrouter) updateMockListing(c *gin.Context) (response.Response,
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to update new listing: %w", err)
|
return nil, fmt.Errorf("failed to update listing: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.StatusOK(), nil
|
return response.StatusOK(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DELETE /:acctID/platforms/:platform/shops/mocks/:shop-id/listings/:listing-id
|
||||||
|
func (s *accountSubrouter) deleteMockListing(c *gin.Context) (response.Response, error) {
|
||||||
|
acctID := auth.GetIdentity(c).Account.AccountID
|
||||||
|
|
||||||
|
var (
|
||||||
|
platform accounts.Platform
|
||||||
|
shopID string
|
||||||
|
listingID string
|
||||||
|
)
|
||||||
|
|
||||||
|
err := param.Path("platform", param.Platform(&platform)).
|
||||||
|
Path("shop-id", param.Text(&shopID)).
|
||||||
|
Path("listing-id", param.Text(&listingID)).
|
||||||
|
Unmarshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.accts.DeleteMockListing(
|
||||||
|
c,
|
||||||
|
accounts.NewAccountIDs(acctID).
|
||||||
|
ShopID(platform, shopID).
|
||||||
|
ListingID(listingID),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to delete listing: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.StatusNoContent(), 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), " "), "_"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"ruben/inventory2/internal/consts"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@@ -46,11 +47,23 @@ func HandleErrors(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
var err error
|
var (
|
||||||
|
err error
|
||||||
|
status int
|
||||||
|
statusFound bool
|
||||||
|
)
|
||||||
for _, e := range c.Errors {
|
for _, e := range c.Errors {
|
||||||
err = errors.Join(err, e)
|
err = errors.Join(err, e)
|
||||||
|
if !statusFound {
|
||||||
|
status, statusFound = mapErrorConstantsToStatus(e)
|
||||||
}
|
}
|
||||||
c.String(http.StatusInternalServerError, err.Error())
|
}
|
||||||
|
|
||||||
|
if !statusFound {
|
||||||
|
status = http.StatusInternalServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
c.String(status, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,3 +144,24 @@ func GetStatusFromError(err error) int {
|
|||||||
|
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mapErrorConstantsToStatus(err error) (int, bool) {
|
||||||
|
for {
|
||||||
|
switch err {
|
||||||
|
case consts.ErrBadRequest:
|
||||||
|
return http.StatusBadRequest, true
|
||||||
|
case consts.ErrNotFound:
|
||||||
|
return http.StatusNotFound, true
|
||||||
|
case consts.ErrConflict:
|
||||||
|
return http.StatusConflict, true
|
||||||
|
default:
|
||||||
|
werr, ok := err.(interface {
|
||||||
|
Unwrap() error
|
||||||
|
})
|
||||||
|
if !ok {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
err = werr.Unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -536,6 +536,9 @@
|
|||||||
.italic {
|
.italic {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
.no-underline {
|
||||||
|
text-decoration-line: none;
|
||||||
|
}
|
||||||
.underline {
|
.underline {
|
||||||
text-decoration-line: underline;
|
text-decoration-line: underline;
|
||||||
}
|
}
|
||||||
@@ -764,6 +767,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.hover\:no-underline {
|
||||||
|
&:hover {
|
||||||
|
@media (hover: hover) {
|
||||||
|
text-decoration-line: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.hover\:underline {
|
.hover\:underline {
|
||||||
&:hover {
|
&:hover {
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
|
|||||||
+37
-17
@@ -297,9 +297,12 @@
|
|||||||
id="listing-{{$listing.ListingID}}"
|
id="listing-{{$listing.ListingID}}"
|
||||||
class="
|
class="
|
||||||
p-[1em]
|
p-[1em]
|
||||||
grid grid-cols-[max-content_1fr] gap-x-[1em]
|
|
||||||
bg-card
|
bg-card
|
||||||
rounded-lg
|
rounded-lg
|
||||||
|
|
||||||
|
flex
|
||||||
|
flex-col
|
||||||
|
items-stretch
|
||||||
"
|
"
|
||||||
_="
|
_="
|
||||||
on click
|
on click
|
||||||
@@ -318,33 +321,50 @@
|
|||||||
remove .border from me
|
remove .border from me
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<span>
|
<table class="mb-[0.5em]" style="display: table;">
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
Name:
|
Name:
|
||||||
</span>
|
</th>
|
||||||
<span class="listing-name">
|
<td class="listing-name">
|
||||||
{{ $listing.Name }}
|
{{ $listing.Name }}
|
||||||
</span>
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<span>
|
<tr>
|
||||||
|
<th>
|
||||||
SKU:
|
SKU:
|
||||||
</span>
|
</th>
|
||||||
<span>
|
<td>
|
||||||
{{ $listing.SKU }}
|
{{ $listing.SKU }}
|
||||||
</span>
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<span>
|
<tr>
|
||||||
|
<th>
|
||||||
Description:
|
Description:
|
||||||
</span>
|
</th>
|
||||||
<span>
|
<td>
|
||||||
{{ $listing.Description }}
|
{{ $listing.Description }}
|
||||||
</span>
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<span>
|
<tr>
|
||||||
|
<th>
|
||||||
Count:
|
Count:
|
||||||
</span>
|
</th>
|
||||||
<span>
|
<td>
|
||||||
{{ $listing.Count }}
|
{{ $listing.Count }}
|
||||||
</span>
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{{ component "button"
|
||||||
|
"Title" "Delete Listing"
|
||||||
|
"Text" (rawHTML "🗑")
|
||||||
|
"HXDelete" (printf "/api/accounts/%d/platforms/amazon/shops/mocks/%s/listings/%s" $acctID $shopID $listing.ListingID)
|
||||||
|
"_" "on click event.preventDefault() then event.stopPropagation()"
|
||||||
|
}}
|
||||||
</li>
|
</li>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
Reference in New Issue
Block a user