frontend-foundation: GOAT front-end Phase 1 — auth, tag management, public tag page (22/22 spec scenarios pass)

This commit is contained in:
2026-08-05 13:46:50 +10:00
parent b846c2c58e
commit 133e375b6b
31 changed files with 1591 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
package db
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
)
type DBTX interface {
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
QueryRow(context.Context, string, ...interface{}) pgx.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
return &Queries{
db: tx,
}
}

View File

@@ -0,0 +1,44 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
package db
import (
"github.com/jackc/pgx/v5/pgtype"
)
type Scan struct {
ID int64 `json:"id"`
TagID int64 `json:"tag_id"`
ScannedAt pgtype.Timestamptz `json:"scanned_at"`
Lat pgtype.Float8 `json:"lat"`
Lng pgtype.Float8 `json:"lng"`
LocationShared bool `json:"location_shared"`
ScannerPhone pgtype.Text `json:"scanner_phone"`
AlertSent bool `json:"alert_sent"`
}
type Tag struct {
ID int64 `json:"id"`
TagCode string `json:"tag_code"`
OwnerID pgtype.Int8 `json:"owner_id"`
Status string `json:"status"`
ItemType pgtype.Text `json:"item_type"`
Description pgtype.Text `json:"description"`
PhotoUrl pgtype.Text `json:"photo_url"`
Phone pgtype.Text `json:"phone"`
Address pgtype.Text `json:"address"`
Notes pgtype.Text `json:"notes"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type User struct {
ID int64 `json:"id"`
Email string `json:"email"`
PasswordHash string `json:"password_hash"`
Name pgtype.Text `json:"name"`
Phone pgtype.Text `json:"phone"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}

View File

@@ -0,0 +1,28 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
package db
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
type Querier interface {
BindTag(ctx context.Context, arg BindTagParams) (Tag, error)
ClearTagOwner(ctx context.Context, id int64) (Tag, error)
CountTagsByOwner(ctx context.Context, ownerID pgtype.Int8) (int64, error)
CreateUser(ctx context.Context, arg CreateUserParams) (User, error)
GetTagByCode(ctx context.Context, tagCode string) (Tag, error)
GetTagByID(ctx context.Context, id int64) (Tag, error)
GetUserByEmail(ctx context.Context, email string) (User, error)
GetUserByID(ctx context.Context, id int64) (User, error)
InsertTag(ctx context.Context, tagCode string) (Tag, error)
ListTagsByOwner(ctx context.Context, ownerID pgtype.Int8) ([]Tag, error)
SetTagStatus(ctx context.Context, arg SetTagStatusParams) error
UpdateTagDetails(ctx context.Context, arg UpdateTagDetailsParams) (Tag, error)
}
var _ Querier = (*Queries)(nil)

View File

@@ -0,0 +1,47 @@
-- name: CreateUser :one
INSERT INTO users (email, password_hash, name, phone)
VALUES ($1, $2, $3, $4)
RETURNING *;
-- name: GetUserByEmail :one
SELECT * FROM users WHERE email = $1;
-- name: GetUserByID :one
SELECT * FROM users WHERE id = $1;
-- name: ListTagsByOwner :many
SELECT * FROM tags WHERE owner_id = $1 ORDER BY created_at DESC;
-- name: GetTagByCode :one
SELECT * FROM tags WHERE tag_code = $1;
-- name: GetTagByID :one
SELECT * FROM tags WHERE id = $1;
-- name: CountTagsByOwner :one
SELECT count(*) FROM tags WHERE owner_id = $1;
-- name: BindTag :one
UPDATE tags
SET owner_id = $1, updated_at = now()
WHERE tag_code = $2 AND owner_id IS NULL AND status = 'unset'
RETURNING *;
-- name: UpdateTagDetails :one
UPDATE tags
SET item_type=$2, description=$3, photo_url=$4, phone=$5, address=$6, notes=$7, status='active', updated_at=now()
WHERE id=$1
RETURNING *;
-- name: ClearTagOwner :one
UPDATE tags
SET owner_id=NULL, status='unset', item_type=NULL, description=NULL, photo_url=NULL, phone=NULL, address=NULL, notes=NULL, updated_at=now()
WHERE id=$1
RETURNING *;
-- name: SetTagStatus :exec
UPDATE tags SET status=$2, updated_at=now() WHERE id=$1;
-- name: InsertTag :one
INSERT INTO tags (tag_code) VALUES ($1)
RETURNING *;

View File

@@ -0,0 +1,319 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: queries.sql
package db
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const bindTag = `-- name: BindTag :one
UPDATE tags
SET owner_id = $1, updated_at = now()
WHERE tag_code = $2 AND owner_id IS NULL AND status = 'unset'
RETURNING id, tag_code, owner_id, status, item_type, description, photo_url, phone, address, notes, created_at, updated_at
`
type BindTagParams struct {
OwnerID pgtype.Int8 `json:"owner_id"`
TagCode string `json:"tag_code"`
}
func (q *Queries) BindTag(ctx context.Context, arg BindTagParams) (Tag, error) {
row := q.db.QueryRow(ctx, bindTag, arg.OwnerID, arg.TagCode)
var i Tag
err := row.Scan(
&i.ID,
&i.TagCode,
&i.OwnerID,
&i.Status,
&i.ItemType,
&i.Description,
&i.PhotoUrl,
&i.Phone,
&i.Address,
&i.Notes,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const clearTagOwner = `-- name: ClearTagOwner :one
UPDATE tags
SET owner_id=NULL, status='unset', item_type=NULL, description=NULL, photo_url=NULL, phone=NULL, address=NULL, notes=NULL, updated_at=now()
WHERE id=$1
RETURNING id, tag_code, owner_id, status, item_type, description, photo_url, phone, address, notes, created_at, updated_at
`
func (q *Queries) ClearTagOwner(ctx context.Context, id int64) (Tag, error) {
row := q.db.QueryRow(ctx, clearTagOwner, id)
var i Tag
err := row.Scan(
&i.ID,
&i.TagCode,
&i.OwnerID,
&i.Status,
&i.ItemType,
&i.Description,
&i.PhotoUrl,
&i.Phone,
&i.Address,
&i.Notes,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const countTagsByOwner = `-- name: CountTagsByOwner :one
SELECT count(*) FROM tags WHERE owner_id = $1
`
func (q *Queries) CountTagsByOwner(ctx context.Context, ownerID pgtype.Int8) (int64, error) {
row := q.db.QueryRow(ctx, countTagsByOwner, ownerID)
var count int64
err := row.Scan(&count)
return count, err
}
const createUser = `-- name: CreateUser :one
INSERT INTO users (email, password_hash, name, phone)
VALUES ($1, $2, $3, $4)
RETURNING id, email, password_hash, name, phone, created_at
`
type CreateUserParams struct {
Email string `json:"email"`
PasswordHash string `json:"password_hash"`
Name pgtype.Text `json:"name"`
Phone pgtype.Text `json:"phone"`
}
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) {
row := q.db.QueryRow(ctx, createUser,
arg.Email,
arg.PasswordHash,
arg.Name,
arg.Phone,
)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.PasswordHash,
&i.Name,
&i.Phone,
&i.CreatedAt,
)
return i, err
}
const getTagByCode = `-- name: GetTagByCode :one
SELECT id, tag_code, owner_id, status, item_type, description, photo_url, phone, address, notes, created_at, updated_at FROM tags WHERE tag_code = $1
`
func (q *Queries) GetTagByCode(ctx context.Context, tagCode string) (Tag, error) {
row := q.db.QueryRow(ctx, getTagByCode, tagCode)
var i Tag
err := row.Scan(
&i.ID,
&i.TagCode,
&i.OwnerID,
&i.Status,
&i.ItemType,
&i.Description,
&i.PhotoUrl,
&i.Phone,
&i.Address,
&i.Notes,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getTagByID = `-- name: GetTagByID :one
SELECT id, tag_code, owner_id, status, item_type, description, photo_url, phone, address, notes, created_at, updated_at FROM tags WHERE id = $1
`
func (q *Queries) GetTagByID(ctx context.Context, id int64) (Tag, error) {
row := q.db.QueryRow(ctx, getTagByID, id)
var i Tag
err := row.Scan(
&i.ID,
&i.TagCode,
&i.OwnerID,
&i.Status,
&i.ItemType,
&i.Description,
&i.PhotoUrl,
&i.Phone,
&i.Address,
&i.Notes,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getUserByEmail = `-- name: GetUserByEmail :one
SELECT id, email, password_hash, name, phone, created_at FROM users WHERE email = $1
`
func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error) {
row := q.db.QueryRow(ctx, getUserByEmail, email)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.PasswordHash,
&i.Name,
&i.Phone,
&i.CreatedAt,
)
return i, err
}
const getUserByID = `-- name: GetUserByID :one
SELECT id, email, password_hash, name, phone, created_at FROM users WHERE id = $1
`
func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
row := q.db.QueryRow(ctx, getUserByID, id)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.PasswordHash,
&i.Name,
&i.Phone,
&i.CreatedAt,
)
return i, err
}
const insertTag = `-- name: InsertTag :one
INSERT INTO tags (tag_code) VALUES ($1)
RETURNING id, tag_code, owner_id, status, item_type, description, photo_url, phone, address, notes, created_at, updated_at
`
func (q *Queries) InsertTag(ctx context.Context, tagCode string) (Tag, error) {
row := q.db.QueryRow(ctx, insertTag, tagCode)
var i Tag
err := row.Scan(
&i.ID,
&i.TagCode,
&i.OwnerID,
&i.Status,
&i.ItemType,
&i.Description,
&i.PhotoUrl,
&i.Phone,
&i.Address,
&i.Notes,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const listTagsByOwner = `-- name: ListTagsByOwner :many
SELECT id, tag_code, owner_id, status, item_type, description, photo_url, phone, address, notes, created_at, updated_at FROM tags WHERE owner_id = $1 ORDER BY created_at DESC
`
func (q *Queries) ListTagsByOwner(ctx context.Context, ownerID pgtype.Int8) ([]Tag, error) {
rows, err := q.db.Query(ctx, listTagsByOwner, ownerID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Tag
for rows.Next() {
var i Tag
if err := rows.Scan(
&i.ID,
&i.TagCode,
&i.OwnerID,
&i.Status,
&i.ItemType,
&i.Description,
&i.PhotoUrl,
&i.Phone,
&i.Address,
&i.Notes,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const setTagStatus = `-- name: SetTagStatus :exec
UPDATE tags SET status=$2, updated_at=now() WHERE id=$1
`
type SetTagStatusParams struct {
ID int64 `json:"id"`
Status string `json:"status"`
}
func (q *Queries) SetTagStatus(ctx context.Context, arg SetTagStatusParams) error {
_, err := q.db.Exec(ctx, setTagStatus, arg.ID, arg.Status)
return err
}
const updateTagDetails = `-- name: UpdateTagDetails :one
UPDATE tags
SET item_type=$2, description=$3, photo_url=$4, phone=$5, address=$6, notes=$7, status='active', updated_at=now()
WHERE id=$1
RETURNING id, tag_code, owner_id, status, item_type, description, photo_url, phone, address, notes, created_at, updated_at
`
type UpdateTagDetailsParams struct {
ID int64 `json:"id"`
ItemType pgtype.Text `json:"item_type"`
Description pgtype.Text `json:"description"`
PhotoUrl pgtype.Text `json:"photo_url"`
Phone pgtype.Text `json:"phone"`
Address pgtype.Text `json:"address"`
Notes pgtype.Text `json:"notes"`
}
func (q *Queries) UpdateTagDetails(ctx context.Context, arg UpdateTagDetailsParams) (Tag, error) {
row := q.db.QueryRow(ctx, updateTagDetails,
arg.ID,
arg.ItemType,
arg.Description,
arg.PhotoUrl,
arg.Phone,
arg.Address,
arg.Notes,
)
var i Tag
err := row.Scan(
&i.ID,
&i.TagCode,
&i.OwnerID,
&i.Status,
&i.ItemType,
&i.Description,
&i.PhotoUrl,
&i.Phone,
&i.Address,
&i.Notes,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}