package templates import ( "fmt" "veola/internal/models" ) type ItemFormData struct { Page IsEdit bool Item models.Item Categories []string Errors []string } func itemSelected(have, want string) bool { return have == want } type marketplaceOpt struct { Value string Label string } func marketplaceOptions() []marketplaceOpt { return []marketplaceOpt{ {"ebay.com", "eBay (US)"}, {"ebay.co.uk", "eBay (UK)"}, {"ebay.de", "eBay (DE)"}, {"ebay.fr", "eBay (FR)"}, {"ebay.com.au", "eBay (AU)"}, {"ebay.ca", "eBay (CA)"}, {"yahoo.co.jp", "Yahoo Auctions JP"}, {"mercari.jp", "Mercari JP"}, } } func isKnownMarketplace(v string) bool { for _, o := range marketplaceOptions() { if o.Value == v { return true } } return false } func itemHasMarketplace(it models.Item, v string) bool { for _, m := range it.Marketplaces { if m == v { return true } } return false } // customMarketplacesCSV returns the comma-separated list of marketplaces on // the item that are NOT in the curated list, so the user can keep editing // unusual values without losing them. func customMarketplacesCSV(it models.Item) string { var custom []string for _, m := range it.Marketplaces { if !isKnownMarketplace(m) { custom = append(custom, m) } } return joinCSV(custom) } func joinCSV(vs []string) string { out := "" for i, v := range vs { if i > 0 { out += ", " } out += v } return out } func itemHasJapanMarketplace(it models.Item) bool { for _, m := range it.Marketplaces { if m == "yahoo.co.jp" || m == "mercari.jp" { return true } } return false } func newCategory(v string, known []string) string { if v == "" { return "" } for _, c := range known { if c == v { return "" } } return v } templ itemFormBody(d ItemFormData) {

if d.IsEdit { Edit { d.Item.Name } } else { Add Item }

if len(d.Errors) > 0 {
} @itemFormInner(d)
} templ itemFormInner(d ItemFormData) {
@CSRFInput(d.CSRFToken)
Each is searched independently; the lowest price across all wins. Cost scales with the number of queries.
At least one of search queries or URL is required.
Blank = alert on every new result.
for _, m := range marketplaceOptions() { }
Yahoo Auctions JP and Mercari JP search in Japanese. English queries return few or no results.
Pick one or more. Each is polled per cycle; one failure does not stop the others.
Drop results below this price. Filters out accessories and obvious junk.
Drop results whose title contains any of these. Case-insensitive substring match.
Advanced
Leave blank to use the configured default for the selected marketplace.
if d.IsEdit { Cancel } else { Cancel Running preview… apify runs can take 30–60s. }
if !d.IsEdit {
} } func formAction(d ItemFormData) templ.SafeURL { if d.IsEdit { return templ.SafeURL(fmt.Sprintf("/items/%d", d.Item.ID)) } return templ.SafeURL("/items/preview") } type pollOpt struct { Minutes int Label string } func pollOptions() []pollOpt { return []pollOpt{ {15, "15 min"}, {30, "30 min"}, {60, "1 hr"}, {120, "2 hr"}, {360, "6 hr"}, {720, "12 hr"}, {1440, "24 hr"}, } } func optFloat(p *float64) string { if p == nil { return "" } return fmt.Sprintf("%.2f", *p) } templ ItemForm(d ItemFormData) { @Layout(d.Page, itemFormBody(d)) }