mirror of
https://github.com/ForFarmTeam/ForFarm.git
synced 2025-12-19 22:14:08 +01:00
Merge branch 'main' into feature-farm-setup
This commit is contained in:
commit
f8752a94de
@ -26,11 +26,12 @@ type api struct {
|
|||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
eventPublisher domain.EventPublisher
|
eventPublisher domain.EventPublisher
|
||||||
|
|
||||||
userRepo domain.UserRepository
|
userRepo domain.UserRepository
|
||||||
cropRepo domain.CroplandRepository
|
cropRepo domain.CroplandRepository
|
||||||
farmRepo domain.FarmRepository
|
farmRepo domain.FarmRepository
|
||||||
plantRepo domain.PlantRepository
|
plantRepo domain.PlantRepository
|
||||||
inventoryRepo domain.InventoryRepository
|
inventoryRepo domain.InventoryRepository
|
||||||
|
harvestRepo domain.HarvestRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAPI(ctx context.Context, logger *slog.Logger, pool *pgxpool.Pool, eventPublisher domain.EventPublisher) *api {
|
func NewAPI(ctx context.Context, logger *slog.Logger, pool *pgxpool.Pool, eventPublisher domain.EventPublisher) *api {
|
||||||
@ -42,6 +43,7 @@ func NewAPI(ctx context.Context, logger *slog.Logger, pool *pgxpool.Pool, eventP
|
|||||||
farmRepository := repository.NewPostgresFarm(pool)
|
farmRepository := repository.NewPostgresFarm(pool)
|
||||||
plantRepository := repository.NewPostgresPlant(pool)
|
plantRepository := repository.NewPostgresPlant(pool)
|
||||||
inventoryRepository := repository.NewPostgresInventory(pool)
|
inventoryRepository := repository.NewPostgresInventory(pool)
|
||||||
|
harvestRepository := repository.NewPostgresHarvest(pool)
|
||||||
|
|
||||||
farmRepository.SetEventPublisher(eventPublisher)
|
farmRepository.SetEventPublisher(eventPublisher)
|
||||||
|
|
||||||
@ -50,11 +52,12 @@ func NewAPI(ctx context.Context, logger *slog.Logger, pool *pgxpool.Pool, eventP
|
|||||||
httpClient: client,
|
httpClient: client,
|
||||||
eventPublisher: eventPublisher,
|
eventPublisher: eventPublisher,
|
||||||
|
|
||||||
userRepo: userRepository,
|
userRepo: userRepository,
|
||||||
cropRepo: croplandRepository,
|
cropRepo: croplandRepository,
|
||||||
farmRepo: farmRepository,
|
farmRepo: farmRepository,
|
||||||
plantRepo: plantRepository,
|
plantRepo: plantRepository,
|
||||||
inventoryRepo: inventoryRepository,
|
inventoryRepo: inventoryRepository,
|
||||||
|
harvestRepo: harvestRepository,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,11 +22,11 @@ func (a *api) registerInventoryRoutes(_ chi.Router, api huma.API) {
|
|||||||
}, a.createInventoryItemHandler)
|
}, a.createInventoryItemHandler)
|
||||||
|
|
||||||
huma.Register(api, huma.Operation{
|
huma.Register(api, huma.Operation{
|
||||||
OperationID: "getInventoryItems",
|
OperationID: "getInventoryItemsByUser",
|
||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
Path: prefix,
|
Path: prefix,
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
}, a.getInventoryItemsHandler)
|
}, a.getInventoryItemsByUserHandler)
|
||||||
|
|
||||||
huma.Register(api, huma.Operation{
|
huma.Register(api, huma.Operation{
|
||||||
OperationID: "getInventoryItem",
|
OperationID: "getInventoryItem",
|
||||||
@ -48,18 +48,66 @@ func (a *api) registerInventoryRoutes(_ chi.Router, api huma.API) {
|
|||||||
Path: prefix + "/{id}",
|
Path: prefix + "/{id}",
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
}, a.deleteInventoryItemHandler)
|
}, a.deleteInventoryItemHandler)
|
||||||
|
|
||||||
|
huma.Register(api, huma.Operation{
|
||||||
|
OperationID: "getInventoryStatus",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: prefix + "/status",
|
||||||
|
Tags: tags,
|
||||||
|
}, a.getInventoryStatusHandler)
|
||||||
|
|
||||||
|
huma.Register(api, huma.Operation{
|
||||||
|
OperationID: "getInventoryCategory",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: prefix + "/category",
|
||||||
|
Tags: tags,
|
||||||
|
}, a.getInventoryCategoryHandler)
|
||||||
|
|
||||||
|
huma.Register(api, huma.Operation{
|
||||||
|
OperationID: "getHarvestUnits",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/harvest/units",
|
||||||
|
Tags: []string{"harvest"},
|
||||||
|
}, a.getHarvestUnitsHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
type InventoryItemResponse struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Category InventoryCategory `json:"category"`
|
||||||
|
Quantity float64 `json:"quantity"`
|
||||||
|
Unit HarvestUnit `json:"unit"`
|
||||||
|
DateAdded time.Time `json:"date_added"`
|
||||||
|
Status InventoryStatus `json:"status"`
|
||||||
|
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InventoryStatus struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InventoryCategory struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HarvestUnit struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateInventoryItemInput struct {
|
type CreateInventoryItemInput struct {
|
||||||
Header string `header:"Authorization" required:"true" example:"Bearer token"`
|
Header string `header:"Authorization" required:"true" example:"Bearer token"`
|
||||||
|
UserID string `header:"user_id" required:"true" example:"user-uuid"`
|
||||||
Body struct {
|
Body struct {
|
||||||
Name string `json:"name" required:"true"`
|
Name string `json:"name" required:"true"`
|
||||||
Category string `json:"category" required:"true"`
|
CategoryID int `json:"category_id" required:"true"`
|
||||||
Type string `json:"type" required:"true"`
|
Quantity float64 `json:"quantity" required:"true"`
|
||||||
Quantity float64 `json:"quantity" required:"true"`
|
UnitID int `json:"unit_id" required:"true"`
|
||||||
Unit string `json:"unit" required:"true"`
|
DateAdded time.Time `json:"date_added" required:"true"`
|
||||||
DateAdded time.Time `json:"date_added" required:"true"`
|
StatusID int `json:"status_id" required:"true"`
|
||||||
Status string `json:"status" required:"true" enum:"In Stock,Low Stock,Out of Stock"`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,15 +117,83 @@ type CreateInventoryItemOutput struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateInventoryItemInput struct {
|
||||||
|
Header string `header:"Authorization" required:"true" example:"Bearer token"`
|
||||||
|
UserID string `header:"user_id" required:"true" example:"user-uuid"`
|
||||||
|
ID string `path:"id"`
|
||||||
|
Body struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
CategoryID int `json:"category_id"`
|
||||||
|
Quantity float64 `json:"quantity"`
|
||||||
|
UnitID int `json:"unit_id"`
|
||||||
|
DateAdded time.Time `json:"date_added"`
|
||||||
|
StatusID int `json:"status_id"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateInventoryItemOutput struct {
|
||||||
|
Body InventoryItemResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetInventoryItemsInput struct {
|
||||||
|
Header string `header:"Authorization" required:"true" example:"Bearer token"`
|
||||||
|
UserID string `header:"user_id" required:"true" example:"user-uuid"`
|
||||||
|
CategoryID int `query:"category_id"`
|
||||||
|
StatusID int `query:"status_id"`
|
||||||
|
StartDate time.Time `query:"start_date" format:"date-time"`
|
||||||
|
EndDate time.Time `query:"end_date" format:"date-time"`
|
||||||
|
SearchQuery string `query:"search"`
|
||||||
|
SortBy string `query:"sort_by" enum:"name,quantity,date_added,created_at"`
|
||||||
|
SortOrder string `query:"sort_order" enum:"asc,desc" default:"desc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetInventoryItemsOutput struct {
|
||||||
|
Body []InventoryItemResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetInventoryItemInput struct {
|
||||||
|
Header string `header:"Authorization" required:"true" example:"Bearer token"`
|
||||||
|
UserID string `header:"user_id" required:"true" example:"user-uuid"`
|
||||||
|
ID string `path:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetInventoryItemOutput struct {
|
||||||
|
Body InventoryItemResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteInventoryItemInput struct {
|
||||||
|
Header string `header:"Authorization" required:"true" example:"Bearer token"`
|
||||||
|
UserID string `header:"user_id" required:"true" example:"user-uuid"`
|
||||||
|
ID string `path:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteInventoryItemOutput struct {
|
||||||
|
Body struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetInventoryStatusOutput struct {
|
||||||
|
Body []InventoryStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetInventoryCategoryOutput struct {
|
||||||
|
Body []InventoryCategory
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetHarvestUnitsOutput struct {
|
||||||
|
Body []HarvestUnit
|
||||||
|
}
|
||||||
|
|
||||||
func (a *api) createInventoryItemHandler(ctx context.Context, input *CreateInventoryItemInput) (*CreateInventoryItemOutput, error) {
|
func (a *api) createInventoryItemHandler(ctx context.Context, input *CreateInventoryItemInput) (*CreateInventoryItemOutput, error) {
|
||||||
item := &domain.InventoryItem{
|
item := &domain.InventoryItem{
|
||||||
Name: input.Body.Name,
|
UserID: input.UserID,
|
||||||
Category: input.Body.Category,
|
Name: input.Body.Name,
|
||||||
Type: input.Body.Type,
|
CategoryID: input.Body.CategoryID,
|
||||||
Quantity: input.Body.Quantity,
|
Quantity: input.Body.Quantity,
|
||||||
Unit: input.Body.Unit,
|
UnitID: input.Body.UnitID,
|
||||||
DateAdded: input.Body.DateAdded,
|
DateAdded: input.Body.DateAdded,
|
||||||
Status: domain.InventoryStatus(input.Body.Status),
|
StatusID: input.Body.StatusID,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := item.Validate(); err != nil {
|
if err := item.Validate(); err != nil {
|
||||||
@ -94,40 +210,11 @@ func (a *api) createInventoryItemHandler(ctx context.Context, input *CreateInven
|
|||||||
}{ID: item.ID}}, nil
|
}{ID: item.ID}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetInventoryItemsInput struct {
|
func (a *api) getInventoryItemsByUserHandler(ctx context.Context, input *GetInventoryItemsInput) (*GetInventoryItemsOutput, error) {
|
||||||
Header string `header:"Authorization" required:"true" example:"Bearer token"`
|
|
||||||
Category string `query:"category"`
|
|
||||||
Type string `query:"type"`
|
|
||||||
Status string `query:"status" enum:"In Stock,Low Stock,Out of Stock"`
|
|
||||||
StartDate time.Time `query:"start_date" format:"date-time"`
|
|
||||||
EndDate time.Time `query:"end_date" format:"date-time"`
|
|
||||||
SearchQuery string `query:"search"`
|
|
||||||
SortBy string `query:"sort_by" enum:"name,category,type,quantity,date_added,status,created_at"`
|
|
||||||
SortOrder string `query:"sort_order" enum:"asc,desc" default:"desc"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type InventoryItemResponse struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Category string `json:"category"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Quantity float64 `json:"quantity"`
|
|
||||||
Unit string `json:"unit"`
|
|
||||||
DateAdded time.Time `json:"date_added"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
|
||||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetInventoryItemsOutput struct {
|
|
||||||
Body []InventoryItemResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *api) getInventoryItemsHandler(ctx context.Context, input *GetInventoryItemsInput) (*GetInventoryItemsOutput, error) {
|
|
||||||
filter := domain.InventoryFilter{
|
filter := domain.InventoryFilter{
|
||||||
Category: input.Category,
|
UserID: input.UserID,
|
||||||
Type: input.Type,
|
CategoryID: input.CategoryID,
|
||||||
Status: domain.InventoryStatus(input.Status),
|
StatusID: input.StatusID,
|
||||||
StartDate: input.StartDate,
|
StartDate: input.StartDate,
|
||||||
EndDate: input.EndDate,
|
EndDate: input.EndDate,
|
||||||
SearchQuery: input.SearchQuery,
|
SearchQuery: input.SearchQuery,
|
||||||
@ -138,7 +225,7 @@ func (a *api) getInventoryItemsHandler(ctx context.Context, input *GetInventoryI
|
|||||||
Direction: input.SortOrder,
|
Direction: input.SortOrder,
|
||||||
}
|
}
|
||||||
|
|
||||||
items, err := a.inventoryRepo.GetWithFilter(ctx, filter, sort)
|
items, err := a.inventoryRepo.GetByUserID(ctx, input.UserID, filter, sort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -146,14 +233,22 @@ func (a *api) getInventoryItemsHandler(ctx context.Context, input *GetInventoryI
|
|||||||
response := make([]InventoryItemResponse, len(items))
|
response := make([]InventoryItemResponse, len(items))
|
||||||
for i, item := range items {
|
for i, item := range items {
|
||||||
response[i] = InventoryItemResponse{
|
response[i] = InventoryItemResponse{
|
||||||
ID: item.ID,
|
ID: item.ID,
|
||||||
Name: item.Name,
|
Name: item.Name,
|
||||||
Category: item.Category,
|
Category: InventoryCategory{
|
||||||
Type: item.Type,
|
ID: item.Category.ID,
|
||||||
Quantity: item.Quantity,
|
Name: item.Category.Name,
|
||||||
Unit: item.Unit,
|
},
|
||||||
|
Quantity: item.Quantity,
|
||||||
|
Unit: HarvestUnit{
|
||||||
|
ID: item.Unit.ID,
|
||||||
|
Name: item.Unit.Name,
|
||||||
|
},
|
||||||
DateAdded: item.DateAdded,
|
DateAdded: item.DateAdded,
|
||||||
Status: string(item.Status),
|
Status: InventoryStatus{
|
||||||
|
ID: item.Status.ID,
|
||||||
|
Name: item.Status.Name,
|
||||||
|
},
|
||||||
CreatedAt: item.CreatedAt,
|
CreatedAt: item.CreatedAt,
|
||||||
UpdatedAt: item.UpdatedAt,
|
UpdatedAt: item.UpdatedAt,
|
||||||
}
|
}
|
||||||
@ -162,55 +257,36 @@ func (a *api) getInventoryItemsHandler(ctx context.Context, input *GetInventoryI
|
|||||||
return &GetInventoryItemsOutput{Body: response}, nil
|
return &GetInventoryItemsOutput{Body: response}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetInventoryItemInput struct {
|
|
||||||
Header string `header:"Authorization" required:"true" example:"Bearer token"`
|
|
||||||
ID string `path:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetInventoryItemOutput struct {
|
|
||||||
Body InventoryItemResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *api) getInventoryItemHandler(ctx context.Context, input *GetInventoryItemInput) (*GetInventoryItemOutput, error) {
|
func (a *api) getInventoryItemHandler(ctx context.Context, input *GetInventoryItemInput) (*GetInventoryItemOutput, error) {
|
||||||
item, err := a.inventoryRepo.GetByID(ctx, input.ID)
|
item, err := a.inventoryRepo.GetByID(ctx, input.ID, input.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &GetInventoryItemOutput{Body: InventoryItemResponse{
|
return &GetInventoryItemOutput{Body: InventoryItemResponse{
|
||||||
ID: item.ID,
|
ID: item.ID,
|
||||||
Name: item.Name,
|
Name: item.Name,
|
||||||
Category: item.Category,
|
Category: InventoryCategory{
|
||||||
Type: item.Type,
|
ID: item.Category.ID,
|
||||||
Quantity: item.Quantity,
|
Name: item.Category.Name,
|
||||||
Unit: item.Unit,
|
},
|
||||||
|
Quantity: item.Quantity,
|
||||||
|
Unit: HarvestUnit{
|
||||||
|
ID: item.Unit.ID,
|
||||||
|
Name: item.Unit.Name,
|
||||||
|
},
|
||||||
DateAdded: item.DateAdded,
|
DateAdded: item.DateAdded,
|
||||||
Status: string(item.Status),
|
Status: InventoryStatus{
|
||||||
|
ID: item.Status.ID,
|
||||||
|
Name: item.Status.Name,
|
||||||
|
},
|
||||||
CreatedAt: item.CreatedAt,
|
CreatedAt: item.CreatedAt,
|
||||||
UpdatedAt: item.UpdatedAt,
|
UpdatedAt: item.UpdatedAt,
|
||||||
}}, nil
|
}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateInventoryItemInput struct {
|
|
||||||
Header string `header:"Authorization" required:"true" example:"Bearer token"`
|
|
||||||
ID string `path:"id"`
|
|
||||||
Body struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Category string `json:"category"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Quantity float64 `json:"quantity"`
|
|
||||||
Unit string `json:"unit"`
|
|
||||||
DateAdded time.Time `json:"date_added"`
|
|
||||||
Status string `json:"status" enum:"In Stock,Low Stock,Out of Stock"`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type UpdateInventoryItemOutput struct {
|
|
||||||
Body InventoryItemResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *api) updateInventoryItemHandler(ctx context.Context, input *UpdateInventoryItemInput) (*UpdateInventoryItemOutput, error) {
|
func (a *api) updateInventoryItemHandler(ctx context.Context, input *UpdateInventoryItemInput) (*UpdateInventoryItemOutput, error) {
|
||||||
item, err := a.inventoryRepo.GetByID(ctx, input.ID)
|
item, err := a.inventoryRepo.GetByID(ctx, input.ID, input.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -218,23 +294,20 @@ func (a *api) updateInventoryItemHandler(ctx context.Context, input *UpdateInven
|
|||||||
if input.Body.Name != "" {
|
if input.Body.Name != "" {
|
||||||
item.Name = input.Body.Name
|
item.Name = input.Body.Name
|
||||||
}
|
}
|
||||||
if input.Body.Category != "" {
|
if input.Body.CategoryID != 0 {
|
||||||
item.Category = input.Body.Category
|
item.CategoryID = input.Body.CategoryID
|
||||||
}
|
|
||||||
if input.Body.Type != "" {
|
|
||||||
item.Type = input.Body.Type
|
|
||||||
}
|
}
|
||||||
if input.Body.Quantity != 0 {
|
if input.Body.Quantity != 0 {
|
||||||
item.Quantity = input.Body.Quantity
|
item.Quantity = input.Body.Quantity
|
||||||
}
|
}
|
||||||
if input.Body.Unit != "" {
|
if input.Body.UnitID != 0 {
|
||||||
item.Unit = input.Body.Unit
|
item.UnitID = input.Body.UnitID
|
||||||
}
|
}
|
||||||
if !input.Body.DateAdded.IsZero() {
|
if !input.Body.DateAdded.IsZero() {
|
||||||
item.DateAdded = input.Body.DateAdded
|
item.DateAdded = input.Body.DateAdded
|
||||||
}
|
}
|
||||||
if input.Body.Status != "" {
|
if input.Body.StatusID != 0 {
|
||||||
item.Status = domain.InventoryStatus(input.Body.Status)
|
item.StatusID = input.Body.StatusID
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := item.Validate(); err != nil {
|
if err := item.Validate(); err != nil {
|
||||||
@ -246,38 +319,35 @@ func (a *api) updateInventoryItemHandler(ctx context.Context, input *UpdateInven
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedItem, err := a.inventoryRepo.GetByID(ctx, input.ID)
|
updatedItem, err := a.inventoryRepo.GetByID(ctx, input.ID, input.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &UpdateInventoryItemOutput{Body: InventoryItemResponse{
|
return &UpdateInventoryItemOutput{Body: InventoryItemResponse{
|
||||||
ID: updatedItem.ID,
|
ID: updatedItem.ID,
|
||||||
Name: updatedItem.Name,
|
Name: updatedItem.Name,
|
||||||
Category: updatedItem.Category,
|
Category: InventoryCategory{
|
||||||
Type: updatedItem.Type,
|
ID: updatedItem.Category.ID,
|
||||||
Quantity: updatedItem.Quantity,
|
Name: updatedItem.Category.Name,
|
||||||
Unit: updatedItem.Unit,
|
},
|
||||||
|
Quantity: updatedItem.Quantity,
|
||||||
|
Unit: HarvestUnit{
|
||||||
|
ID: updatedItem.Unit.ID,
|
||||||
|
Name: updatedItem.Unit.Name,
|
||||||
|
},
|
||||||
DateAdded: updatedItem.DateAdded,
|
DateAdded: updatedItem.DateAdded,
|
||||||
Status: string(updatedItem.Status),
|
Status: InventoryStatus{
|
||||||
|
ID: updatedItem.Status.ID,
|
||||||
|
Name: updatedItem.Status.Name,
|
||||||
|
},
|
||||||
CreatedAt: updatedItem.CreatedAt,
|
CreatedAt: updatedItem.CreatedAt,
|
||||||
UpdatedAt: updatedItem.UpdatedAt,
|
UpdatedAt: updatedItem.UpdatedAt,
|
||||||
}}, nil
|
}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeleteInventoryItemInput struct {
|
|
||||||
Header string `header:"Authorization" required:"true" example:"Bearer token"`
|
|
||||||
ID string `path:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeleteInventoryItemOutput struct {
|
|
||||||
Body struct {
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *api) deleteInventoryItemHandler(ctx context.Context, input *DeleteInventoryItemInput) (*DeleteInventoryItemOutput, error) {
|
func (a *api) deleteInventoryItemHandler(ctx context.Context, input *DeleteInventoryItemInput) (*DeleteInventoryItemOutput, error) {
|
||||||
err := a.inventoryRepo.Delete(ctx, input.ID)
|
err := a.inventoryRepo.Delete(ctx, input.ID, input.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -286,3 +356,54 @@ func (a *api) deleteInventoryItemHandler(ctx context.Context, input *DeleteInven
|
|||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
}{Message: "Inventory item deleted successfully"}}, nil
|
}{Message: "Inventory item deleted successfully"}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *api) getInventoryStatusHandler(ctx context.Context, input *struct{}) (*GetInventoryStatusOutput, error) {
|
||||||
|
statuses, err := a.inventoryRepo.GetStatuses(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := make([]InventoryStatus, len(statuses))
|
||||||
|
for i, status := range statuses {
|
||||||
|
response[i] = InventoryStatus{
|
||||||
|
ID: status.ID,
|
||||||
|
Name: status.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &GetInventoryStatusOutput{Body: response}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *api) getInventoryCategoryHandler(ctx context.Context, input *struct{}) (*GetInventoryCategoryOutput, error) {
|
||||||
|
categories, err := a.inventoryRepo.GetCategories(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := make([]InventoryCategory, len(categories))
|
||||||
|
for i, category := range categories {
|
||||||
|
response[i] = InventoryCategory{
|
||||||
|
ID: category.ID,
|
||||||
|
Name: category.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &GetInventoryCategoryOutput{Body: response}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *api) getHarvestUnitsHandler(ctx context.Context, input *struct{}) (*GetHarvestUnitsOutput, error) {
|
||||||
|
units, err := a.harvestRepo.GetUnits(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := make([]HarvestUnit, len(units))
|
||||||
|
for i, unit := range units {
|
||||||
|
response[i] = HarvestUnit{
|
||||||
|
ID: unit.ID,
|
||||||
|
Name: unit.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &GetHarvestUnitsOutput{Body: response}, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -7,31 +7,41 @@ import (
|
|||||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InventoryStatus string
|
type InventoryStatus struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
type InventoryCategory struct {
|
||||||
StatusInStock InventoryStatus = "In Stock"
|
ID int `json:"id"`
|
||||||
StatusLowStock InventoryStatus = "Low Stock"
|
Name string `json:"name"`
|
||||||
StatusOutOfStock InventoryStatus = "Out of Stock"
|
}
|
||||||
)
|
|
||||||
|
type HarvestUnit struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
type InventoryItem struct {
|
type InventoryItem struct {
|
||||||
ID string
|
ID string
|
||||||
Name string
|
UserID string
|
||||||
Category string
|
Name string
|
||||||
Type string
|
CategoryID int
|
||||||
Quantity float64
|
Category InventoryCategory
|
||||||
Unit string
|
Quantity float64
|
||||||
DateAdded time.Time
|
UnitID int
|
||||||
Status InventoryStatus
|
Unit HarvestUnit
|
||||||
CreatedAt time.Time
|
DateAdded time.Time
|
||||||
UpdatedAt time.Time
|
StatusID int
|
||||||
|
Status InventoryStatus
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type InventoryFilter struct {
|
type InventoryFilter struct {
|
||||||
Category string
|
UserID string
|
||||||
Type string
|
CategoryID int
|
||||||
Status InventoryStatus
|
StatusID int
|
||||||
StartDate time.Time
|
StartDate time.Time
|
||||||
EndDate time.Time
|
EndDate time.Time
|
||||||
SearchQuery string
|
SearchQuery string
|
||||||
@ -44,18 +54,26 @@ type InventorySort struct {
|
|||||||
|
|
||||||
func (i *InventoryItem) Validate() error {
|
func (i *InventoryItem) Validate() error {
|
||||||
return validation.ValidateStruct(i,
|
return validation.ValidateStruct(i,
|
||||||
|
validation.Field(&i.UserID, validation.Required),
|
||||||
validation.Field(&i.Name, validation.Required),
|
validation.Field(&i.Name, validation.Required),
|
||||||
validation.Field(&i.Category, validation.Required),
|
validation.Field(&i.CategoryID, validation.Required),
|
||||||
validation.Field(&i.Type, validation.Required),
|
|
||||||
validation.Field(&i.Quantity, validation.Required, validation.Min(0.0)),
|
validation.Field(&i.Quantity, validation.Required, validation.Min(0.0)),
|
||||||
validation.Field(&i.Unit, validation.Required),
|
validation.Field(&i.UnitID, validation.Required),
|
||||||
validation.Field(&i.Status, validation.Required, validation.In(StatusInStock, StatusLowStock, StatusOutOfStock)),
|
validation.Field(&i.StatusID, validation.Required),
|
||||||
|
validation.Field(&i.DateAdded, validation.Required),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type InventoryRepository interface {
|
type InventoryRepository interface {
|
||||||
GetByID(ctx context.Context, id string) (InventoryItem, error)
|
GetByID(ctx context.Context, id, userID string) (InventoryItem, error)
|
||||||
GetWithFilter(ctx context.Context, filter InventoryFilter, sort InventorySort) ([]InventoryItem, error)
|
GetByUserID(ctx context.Context, userID string, filter InventoryFilter, sort InventorySort) ([]InventoryItem, error)
|
||||||
|
GetAll(ctx context.Context) ([]InventoryItem, error)
|
||||||
CreateOrUpdate(ctx context.Context, item *InventoryItem) error
|
CreateOrUpdate(ctx context.Context, item *InventoryItem) error
|
||||||
Delete(ctx context.Context, id string) error
|
Delete(ctx context.Context, id, userID string) error
|
||||||
|
GetStatuses(ctx context.Context) ([]InventoryStatus, error)
|
||||||
|
GetCategories(ctx context.Context) ([]InventoryCategory, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type HarvestRepository interface {
|
||||||
|
GetUnits(ctx context.Context) ([]HarvestUnit, error)
|
||||||
}
|
}
|
||||||
|
|||||||
34
backend/internal/repository/postgres_harvest.go
Normal file
34
backend/internal/repository/postgres_harvest.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/forfarm/backend/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type postgresHarvestRepository struct {
|
||||||
|
conn Connection
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPostgresHarvest(conn Connection) domain.HarvestRepository {
|
||||||
|
return &postgresHarvestRepository{conn: conn}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *postgresHarvestRepository) GetUnits(ctx context.Context) ([]domain.HarvestUnit, error) {
|
||||||
|
query := `SELECT id, name FROM harvest_units ORDER BY id`
|
||||||
|
rows, err := p.conn.Query(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var units []domain.HarvestUnit
|
||||||
|
for rows.Next() {
|
||||||
|
var u domain.HarvestUnit
|
||||||
|
if err := rows.Scan(&u.ID, &u.Name); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
units = append(units, u)
|
||||||
|
}
|
||||||
|
return units, nil
|
||||||
|
}
|
||||||
@ -29,13 +29,13 @@ func (p *postgresInventoryRepository) fetch(ctx context.Context, query string, a
|
|||||||
var i domain.InventoryItem
|
var i domain.InventoryItem
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
|
&i.UserID,
|
||||||
&i.Name,
|
&i.Name,
|
||||||
&i.Category,
|
&i.CategoryID,
|
||||||
&i.Type,
|
|
||||||
&i.Quantity,
|
&i.Quantity,
|
||||||
&i.Unit,
|
&i.UnitID,
|
||||||
&i.DateAdded,
|
&i.DateAdded,
|
||||||
&i.Status,
|
&i.StatusID,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@ -46,80 +46,115 @@ func (p *postgresInventoryRepository) fetch(ctx context.Context, query string, a
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *postgresInventoryRepository) GetByID(ctx context.Context, id string) (domain.InventoryItem, error) {
|
func (p *postgresInventoryRepository) GetByID(ctx context.Context, id, userID string) (domain.InventoryItem, error) {
|
||||||
query := `
|
query := `
|
||||||
SELECT id, name, category, type, quantity, unit, date_added, status, created_at, updated_at
|
SELECT
|
||||||
FROM inventory_items
|
i.id, i.user_id, i.name, i.category_id, i.quantity, i.unit_id,
|
||||||
WHERE id = $1`
|
i.date_added, i.status_id, i.created_at, i.updated_at,
|
||||||
|
c.name as category_name,
|
||||||
|
s.name as status_name,
|
||||||
|
u.name as unit_name
|
||||||
|
FROM inventory_items i
|
||||||
|
LEFT JOIN inventory_category c ON i.category_id = c.id
|
||||||
|
LEFT JOIN inventory_status s ON i.status_id = s.id
|
||||||
|
LEFT JOIN harvest_units u ON i.unit_id = u.id
|
||||||
|
WHERE i.id = $1 AND i.user_id = $2`
|
||||||
|
|
||||||
items, err := p.fetch(ctx, query, id)
|
rows, err := p.conn.Query(ctx, query, id, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.InventoryItem{}, err
|
return domain.InventoryItem{}, err
|
||||||
}
|
}
|
||||||
if len(items) == 0 {
|
defer rows.Close()
|
||||||
|
|
||||||
|
if !rows.Next() {
|
||||||
return domain.InventoryItem{}, domain.ErrNotFound
|
return domain.InventoryItem{}, domain.ErrNotFound
|
||||||
}
|
}
|
||||||
return items[0], nil
|
|
||||||
|
var item domain.InventoryItem
|
||||||
|
err = rows.Scan(
|
||||||
|
&item.ID,
|
||||||
|
&item.UserID,
|
||||||
|
&item.Name,
|
||||||
|
&item.CategoryID,
|
||||||
|
&item.Quantity,
|
||||||
|
&item.UnitID,
|
||||||
|
&item.DateAdded,
|
||||||
|
&item.StatusID,
|
||||||
|
&item.CreatedAt,
|
||||||
|
&item.UpdatedAt,
|
||||||
|
&item.Category.Name,
|
||||||
|
&item.Status.Name,
|
||||||
|
&item.Unit.Name,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return domain.InventoryItem{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *postgresInventoryRepository) GetWithFilter(ctx context.Context, filter domain.InventoryFilter, sort domain.InventorySort) ([]domain.InventoryItem, error) {
|
func (p *postgresInventoryRepository) GetByUserID(
|
||||||
|
ctx context.Context,
|
||||||
|
userID string,
|
||||||
|
filter domain.InventoryFilter,
|
||||||
|
sort domain.InventorySort,
|
||||||
|
) ([]domain.InventoryItem, error) {
|
||||||
var query strings.Builder
|
var query strings.Builder
|
||||||
args := []interface{}{}
|
args := []interface{}{userID}
|
||||||
argPos := 1
|
argPos := 2
|
||||||
|
|
||||||
query.WriteString(`
|
query.WriteString(`
|
||||||
SELECT id, name, category, type, quantity, unit, date_added, status, created_at, updated_at
|
SELECT
|
||||||
FROM inventory_items
|
i.id, i.user_id, i.name, i.category_id, i.quantity, i.unit_id,
|
||||||
WHERE 1=1`)
|
i.date_added, i.status_id, i.created_at, i.updated_at,
|
||||||
|
c.name as category_name,
|
||||||
|
s.name as status_name,
|
||||||
|
u.name as unit_name
|
||||||
|
FROM inventory_items i
|
||||||
|
LEFT JOIN inventory_category c ON i.category_id = c.id
|
||||||
|
LEFT JOIN inventory_status s ON i.status_id = s.id
|
||||||
|
LEFT JOIN harvest_units u ON i.unit_id = u.id
|
||||||
|
WHERE i.user_id = $1`)
|
||||||
|
|
||||||
if filter.Category != "" {
|
if filter.CategoryID != 0 {
|
||||||
query.WriteString(fmt.Sprintf(" AND category = $%d", argPos))
|
query.WriteString(fmt.Sprintf(" AND i.category_id = $%d", argPos))
|
||||||
args = append(args, filter.Category)
|
args = append(args, filter.CategoryID)
|
||||||
argPos++
|
argPos++
|
||||||
}
|
}
|
||||||
|
|
||||||
if filter.Type != "" {
|
if filter.StatusID != 0 {
|
||||||
query.WriteString(fmt.Sprintf(" AND type = $%d", argPos))
|
query.WriteString(fmt.Sprintf(" AND i.status_id = $%d", argPos))
|
||||||
args = append(args, filter.Type)
|
args = append(args, filter.StatusID)
|
||||||
argPos++
|
|
||||||
}
|
|
||||||
|
|
||||||
if filter.Status != "" {
|
|
||||||
query.WriteString(fmt.Sprintf(" AND status = $%d", argPos))
|
|
||||||
args = append(args, filter.Status)
|
|
||||||
argPos++
|
argPos++
|
||||||
}
|
}
|
||||||
|
|
||||||
if !filter.StartDate.IsZero() {
|
if !filter.StartDate.IsZero() {
|
||||||
query.WriteString(fmt.Sprintf(" AND date_added >= $%d", argPos))
|
query.WriteString(fmt.Sprintf(" AND i.date_added >= $%d", argPos))
|
||||||
args = append(args, filter.StartDate)
|
args = append(args, filter.StartDate)
|
||||||
argPos++
|
argPos++
|
||||||
}
|
}
|
||||||
|
|
||||||
if !filter.EndDate.IsZero() {
|
if !filter.EndDate.IsZero() {
|
||||||
query.WriteString(fmt.Sprintf(" AND date_added <= $%d", argPos))
|
query.WriteString(fmt.Sprintf(" AND i.date_added <= $%d", argPos))
|
||||||
args = append(args, filter.EndDate)
|
args = append(args, filter.EndDate)
|
||||||
argPos++
|
argPos++
|
||||||
}
|
}
|
||||||
|
|
||||||
if filter.SearchQuery != "" {
|
if filter.SearchQuery != "" {
|
||||||
query.WriteString(fmt.Sprintf(" AND name ILIKE $%d", argPos))
|
query.WriteString(fmt.Sprintf(" AND i.name ILIKE $%d", argPos))
|
||||||
args = append(args, "%"+filter.SearchQuery+"%")
|
args = append(args, "%"+filter.SearchQuery+"%")
|
||||||
argPos++
|
argPos++
|
||||||
}
|
}
|
||||||
|
|
||||||
if sort.Field == "" {
|
if sort.Field == "" {
|
||||||
sort.Field = "date_added"
|
sort.Field = "i.date_added"
|
||||||
sort.Direction = "desc"
|
sort.Direction = "desc"
|
||||||
}
|
}
|
||||||
|
|
||||||
validSortFields := map[string]bool{
|
validSortFields := map[string]bool{
|
||||||
"name": true,
|
"name": true,
|
||||||
"category": true,
|
|
||||||
"type": true,
|
|
||||||
"quantity": true,
|
"quantity": true,
|
||||||
"date_added": true,
|
"date_added": true,
|
||||||
"status": true,
|
|
||||||
"created_at": true,
|
"created_at": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +167,53 @@ func (p *postgresInventoryRepository) GetWithFilter(ctx context.Context, filter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.fetch(ctx, query.String(), args...)
|
rows, err := p.conn.Query(ctx, query.String(), args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var items []domain.InventoryItem
|
||||||
|
for rows.Next() {
|
||||||
|
var item domain.InventoryItem
|
||||||
|
err := rows.Scan(
|
||||||
|
&item.ID,
|
||||||
|
&item.UserID,
|
||||||
|
&item.Name,
|
||||||
|
&item.CategoryID,
|
||||||
|
&item.Quantity,
|
||||||
|
&item.UnitID,
|
||||||
|
&item.DateAdded,
|
||||||
|
&item.StatusID,
|
||||||
|
&item.CreatedAt,
|
||||||
|
&item.UpdatedAt,
|
||||||
|
&item.Category.Name,
|
||||||
|
&item.Status.Name,
|
||||||
|
&item.Unit.Name,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *postgresInventoryRepository) GetAll(ctx context.Context) ([]domain.InventoryItem, error) {
|
||||||
|
query := `
|
||||||
|
SELECT
|
||||||
|
i.id, i.user_id, i.name, i.category_id, i.quantity, i.unit_id,
|
||||||
|
i.date_added, i.status_id, i.created_at, i.updated_at,
|
||||||
|
c.name as category_name,
|
||||||
|
s.name as status_name,
|
||||||
|
u.name as unit_name
|
||||||
|
FROM inventory_items i
|
||||||
|
LEFT JOIN inventory_category c ON i.category_id = c.id
|
||||||
|
LEFT JOIN inventory_status s ON i.status_id = s.id
|
||||||
|
LEFT JOIN harvest_units u ON i.unit_id = u.id
|
||||||
|
ORDER BY i.created_at DESC`
|
||||||
|
return p.fetch(ctx, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *postgresInventoryRepository) CreateOrUpdate(ctx context.Context, item *domain.InventoryItem) error {
|
func (p *postgresInventoryRepository) CreateOrUpdate(ctx context.Context, item *domain.InventoryItem) error {
|
||||||
@ -143,19 +224,19 @@ func (p *postgresInventoryRepository) CreateOrUpdate(ctx context.Context, item *
|
|||||||
item.CreatedAt = now
|
item.CreatedAt = now
|
||||||
query := `
|
query := `
|
||||||
INSERT INTO inventory_items
|
INSERT INTO inventory_items
|
||||||
(id, name, category, type, quantity, unit, date_added, status, created_at, updated_at)
|
(id, user_id, name, category_id, quantity, unit_id, date_added, status_id, created_at, updated_at)
|
||||||
VALUES (gen_random_uuid(), $1, $2, $3, $4, $5, $6, $7, $8, $9)
|
VALUES (gen_random_uuid(), $1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||||
RETURNING id`
|
RETURNING id`
|
||||||
return p.conn.QueryRow(
|
return p.conn.QueryRow(
|
||||||
ctx,
|
ctx,
|
||||||
query,
|
query,
|
||||||
|
item.UserID,
|
||||||
item.Name,
|
item.Name,
|
||||||
item.Category,
|
item.CategoryID,
|
||||||
item.Type,
|
|
||||||
item.Quantity,
|
item.Quantity,
|
||||||
item.Unit,
|
item.UnitID,
|
||||||
item.DateAdded,
|
item.DateAdded,
|
||||||
item.Status,
|
item.StatusID,
|
||||||
item.CreatedAt,
|
item.CreatedAt,
|
||||||
item.UpdatedAt,
|
item.UpdatedAt,
|
||||||
).Scan(&item.ID)
|
).Scan(&item.ID)
|
||||||
@ -164,33 +245,70 @@ func (p *postgresInventoryRepository) CreateOrUpdate(ctx context.Context, item *
|
|||||||
query := `
|
query := `
|
||||||
UPDATE inventory_items
|
UPDATE inventory_items
|
||||||
SET name = $1,
|
SET name = $1,
|
||||||
category = $2,
|
category_id = $2,
|
||||||
type = $3,
|
quantity = $3,
|
||||||
quantity = $4,
|
unit_id = $4,
|
||||||
unit = $5,
|
date_added = $5,
|
||||||
date_added = $6,
|
status_id = $6,
|
||||||
status = $7,
|
updated_at = $7
|
||||||
updated_at = $8
|
WHERE id = $8 AND user_id = $9
|
||||||
WHERE id = $9
|
|
||||||
RETURNING id`
|
RETURNING id`
|
||||||
|
|
||||||
return p.conn.QueryRow(
|
return p.conn.QueryRow(
|
||||||
ctx,
|
ctx,
|
||||||
query,
|
query,
|
||||||
item.Name,
|
item.Name,
|
||||||
item.Category,
|
item.CategoryID,
|
||||||
item.Type,
|
|
||||||
item.Quantity,
|
item.Quantity,
|
||||||
item.Unit,
|
item.UnitID,
|
||||||
item.DateAdded,
|
item.DateAdded,
|
||||||
item.Status,
|
item.StatusID,
|
||||||
item.UpdatedAt,
|
item.UpdatedAt,
|
||||||
item.ID,
|
item.ID,
|
||||||
|
item.UserID,
|
||||||
).Scan(&item.ID)
|
).Scan(&item.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *postgresInventoryRepository) Delete(ctx context.Context, id string) error {
|
func (p *postgresInventoryRepository) Delete(ctx context.Context, id, userID string) error {
|
||||||
query := `DELETE FROM inventory_items WHERE id = $1`
|
query := `DELETE FROM inventory_items WHERE id = $1 AND user_id = $2`
|
||||||
_, err := p.conn.Exec(ctx, query, id)
|
_, err := p.conn.Exec(ctx, query, id, userID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *postgresInventoryRepository) GetStatuses(ctx context.Context) ([]domain.InventoryStatus, error) {
|
||||||
|
query := `SELECT id, name FROM inventory_status ORDER BY id`
|
||||||
|
rows, err := p.conn.Query(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var statuses []domain.InventoryStatus
|
||||||
|
for rows.Next() {
|
||||||
|
var s domain.InventoryStatus
|
||||||
|
if err := rows.Scan(&s.ID, &s.Name); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
statuses = append(statuses, s)
|
||||||
|
}
|
||||||
|
return statuses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *postgresInventoryRepository) GetCategories(ctx context.Context) ([]domain.InventoryCategory, error) {
|
||||||
|
query := `SELECT id, name FROM inventory_category ORDER BY id`
|
||||||
|
rows, err := p.conn.Query(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var categories []domain.InventoryCategory
|
||||||
|
for rows.Next() {
|
||||||
|
var c domain.InventoryCategory
|
||||||
|
if err := rows.Scan(&c.ID, &c.Name); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
categories = append(categories, c)
|
||||||
|
}
|
||||||
|
return categories, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
-- +goose Up
|
-- +goose Up
|
||||||
CREATE TABLE inventory_items (
|
CREATE TABLE inventory_items (
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
user_id UUID NOT NULL,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
category TEXT NOT NULL,
|
category TEXT NOT NULL,
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL,
|
||||||
@ -9,8 +10,11 @@ CREATE TABLE inventory_items (
|
|||||||
date_added TIMESTAMPTZ NOT NULL,
|
date_added TIMESTAMPTZ NOT NULL,
|
||||||
status TEXT NOT NULL,
|
status TEXT NOT NULL,
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
CONSTRAINT fk_inventory_items_user FOREIGN KEY (user_id) REFERENCES users(uuid) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_inventory_items_category ON inventory_items(category);
|
-- Create indexes
|
||||||
CREATE INDEX idx_inventory_items_status ON inventory_items(status);
|
CREATE INDEX idx_inventory_items_user_id ON inventory_items(user_id);
|
||||||
|
CREATE INDEX idx_inventory_items_user_category ON inventory_items(user_id, category);
|
||||||
|
CREATE INDEX idx_inventory_items_user_status ON inventory_items(user_id, status);
|
||||||
|
|||||||
5
backend/migrations/00005_create_inventory_status.sql
Normal file
5
backend/migrations/00005_create_inventory_status.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
-- +goose Up
|
||||||
|
CREATE TABLE inventory_status (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL UNIQUE
|
||||||
|
);
|
||||||
15
backend/migrations/00006_modify_inventory_table.sql
Normal file
15
backend/migrations/00006_modify_inventory_table.sql
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
-- +goose Up
|
||||||
|
ALTER TABLE inventory_items
|
||||||
|
ADD COLUMN status_id INT;
|
||||||
|
|
||||||
|
UPDATE inventory_items
|
||||||
|
SET status_id = (SELECT id FROM inventory_status WHERE name = inventory_items.status);
|
||||||
|
|
||||||
|
ALTER TABLE inventory_items
|
||||||
|
DROP COLUMN status;
|
||||||
|
|
||||||
|
ALTER TABLE inventory_items
|
||||||
|
ADD CONSTRAINT fk_inventory_items_status FOREIGN KEY (status_id) REFERENCES inventory_status(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
CREATE INDEX idx_inventory_items_status_id ON inventory_items(status_id);
|
||||||
|
|
||||||
7
backend/migrations/00007_create_inventory_status.sql
Normal file
7
backend/migrations/00007_create_inventory_status.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- Insert default statuses into the inventory_status table
|
||||||
|
INSERT INTO inventory_status (name)
|
||||||
|
VALUES
|
||||||
|
('In Stock'),
|
||||||
|
('Low Stock'),
|
||||||
|
('Out Of Stock');
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- Step 1: Create inventory_category table
|
||||||
|
CREATE TABLE inventory_category (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Step 2: Insert sample categories
|
||||||
|
INSERT INTO inventory_category (name)
|
||||||
|
VALUES
|
||||||
|
('Seeds'),
|
||||||
|
('Tools'),
|
||||||
|
('Chemicals');
|
||||||
|
|
||||||
|
-- Step 3: Add category_id column to inventory_items
|
||||||
|
ALTER TABLE inventory_items
|
||||||
|
ADD COLUMN category_id INT;
|
||||||
|
|
||||||
|
-- Step 4: Link inventory_items to inventory_category
|
||||||
|
ALTER TABLE inventory_items
|
||||||
|
ADD CONSTRAINT fk_inventory_category FOREIGN KEY (category_id) REFERENCES inventory_category(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
-- Step 5: Remove old columns (type, category, unit) from inventory_items
|
||||||
|
ALTER TABLE inventory_items
|
||||||
|
DROP COLUMN type,
|
||||||
|
DROP COLUMN category,
|
||||||
|
DROP COLUMN unit;
|
||||||
|
|
||||||
|
-- Step 6: Add unit_id column to inventory_items
|
||||||
|
ALTER TABLE inventory_items
|
||||||
|
ADD COLUMN unit_id INT;
|
||||||
|
|
||||||
|
-- Step 7: Link inventory_items to harvest_units
|
||||||
|
ALTER TABLE inventory_items
|
||||||
|
ADD CONSTRAINT fk_inventory_unit FOREIGN KEY (unit_id) REFERENCES harvest_units(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
-- Step 8: Insert new unit values into harvest_units
|
||||||
|
INSERT INTO harvest_units (name)
|
||||||
|
VALUES
|
||||||
|
('Tonne'),
|
||||||
|
('KG');
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- Reverse Step 8: Remove inserted unit values
|
||||||
|
DELETE FROM harvest_units WHERE name IN ('Tonne', 'KG');
|
||||||
|
|
||||||
|
-- Reverse Step 7: Remove the foreign key constraint
|
||||||
|
ALTER TABLE inventory_items
|
||||||
|
DROP CONSTRAINT fk_inventory_unit;
|
||||||
|
|
||||||
|
-- Reverse Step 6: Remove unit_id column from inventory_items
|
||||||
|
ALTER TABLE inventory_items
|
||||||
|
DROP COLUMN unit_id;
|
||||||
|
|
||||||
|
-- Reverse Step 5: Add back type, category, and unit columns
|
||||||
|
ALTER TABLE inventory_items
|
||||||
|
ADD COLUMN type TEXT NOT NULL,
|
||||||
|
ADD COLUMN category TEXT NOT NULL,
|
||||||
|
ADD COLUMN unit TEXT NOT NULL;
|
||||||
|
|
||||||
|
-- Reverse Step 4: Remove foreign key constraint from inventory_items
|
||||||
|
ALTER TABLE inventory_items
|
||||||
|
DROP CONSTRAINT fk_inventory_category;
|
||||||
|
|
||||||
|
-- Reverse Step 3: Remove category_id column from inventory_items
|
||||||
|
ALTER TABLE inventory_items
|
||||||
|
DROP COLUMN category_id;
|
||||||
|
|
||||||
|
-- Reverse Step 2: Drop inventory_category table
|
||||||
|
DROP TABLE inventory_category;
|
||||||
@ -1,11 +1,29 @@
|
|||||||
import axiosInstance from "./config";
|
import axiosInstance from "./config";
|
||||||
import type { InventoryItem, CreateInventoryItemInput } from "@/types";
|
import type {
|
||||||
|
InventoryItem,
|
||||||
|
CreateInventoryItemInput,
|
||||||
|
InventoryItemStatus,
|
||||||
|
} from "@/types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simulates an API call to fetch inventory items.
|
* Simulates an API call to fetch inventory items.
|
||||||
* Waits for a simulated delay and then attempts an axios GET request.
|
* Waits for a simulated delay and then attempts an axios GET request.
|
||||||
* If the request fails, returns fallback dummy data.
|
* If the request fails, returns fallback dummy data.
|
||||||
|
*
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
export async function fetchInventoryStatus(): Promise<InventoryItemStatus[]> {
|
||||||
|
try {
|
||||||
|
const response = await axiosInstance.get<InventoryItemStatus[]>(
|
||||||
|
"/inventory/status"
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching inventory status:", error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchInventoryItems(): Promise<InventoryItem[]> {
|
export async function fetchInventoryItems(): Promise<InventoryItem[]> {
|
||||||
try {
|
try {
|
||||||
const response = await axiosInstance.get<InventoryItem[]>("/api/inventory");
|
const response = await axiosInstance.get<InventoryItem[]>("/api/inventory");
|
||||||
@ -51,7 +69,7 @@ export async function fetchInventoryItems(): Promise<InventoryItem[]> {
|
|||||||
quantity: 150,
|
quantity: 150,
|
||||||
unit: "kg",
|
unit: "kg",
|
||||||
lastUpdated: "2023-03-15",
|
lastUpdated: "2023-03-15",
|
||||||
status: "In Stock",
|
status: "Out Of Stock",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
|
|||||||
@ -36,14 +36,32 @@ import { cn } from "@/lib/utils";
|
|||||||
// import { updateInventoryItem } from "@/api/inventory";
|
// import { updateInventoryItem } from "@/api/inventory";
|
||||||
// import type { UpdateInventoryItemInput } from "@/types";
|
// import type { UpdateInventoryItemInput } from "@/types";
|
||||||
|
|
||||||
export function EditInventoryItem() {
|
export interface EditInventoryItemProps {
|
||||||
const [date, setDate] = useState<Date | undefined>();
|
id: string;
|
||||||
|
name: string;
|
||||||
|
category: string;
|
||||||
|
status: string;
|
||||||
|
type: string;
|
||||||
|
unit: string;
|
||||||
|
quantity: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EditInventoryItem({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
category,
|
||||||
|
status,
|
||||||
|
type,
|
||||||
|
unit,
|
||||||
|
quantity,
|
||||||
|
}: EditInventoryItemProps) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [itemName, setItemName] = useState("");
|
const [itemName, setItemName] = useState(name);
|
||||||
const [itemType, setItemType] = useState("");
|
const [itemType, setItemType] = useState(type);
|
||||||
const [itemCategory, setItemCategory] = useState("");
|
const [itemCategory, setItemCategory] = useState(category);
|
||||||
const [itemQuantity, setItemQuantity] = useState(0);
|
const [itemQuantity, setItemQuantity] = useState(quantity);
|
||||||
const [itemUnit, setItemUnit] = useState("");
|
const [itemUnit, setItemUnit] = useState(unit);
|
||||||
|
const [itemStatus, setItemStatus] = useState(status);
|
||||||
|
|
||||||
// const queryClient = useQueryClient();
|
// const queryClient = useQueryClient();
|
||||||
|
|
||||||
@ -103,7 +121,7 @@ export function EditInventoryItem() {
|
|||||||
<Label htmlFor="type" className="text-right">
|
<Label htmlFor="type" className="text-right">
|
||||||
Type
|
Type
|
||||||
</Label>
|
</Label>
|
||||||
<Select value={itemType} onValueChange={setItemType}>
|
<Select value={itemType.toLowerCase()} onValueChange={setItemType}>
|
||||||
<SelectTrigger className="col-span-3">
|
<SelectTrigger className="col-span-3">
|
||||||
<SelectValue placeholder="Select type" />
|
<SelectValue placeholder="Select type" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
@ -116,6 +134,27 @@ export function EditInventoryItem() {
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
|
<Label htmlFor="type" className="text-right">
|
||||||
|
Status
|
||||||
|
</Label>
|
||||||
|
<Select
|
||||||
|
value={itemStatus.toLowerCase()}
|
||||||
|
onValueChange={setItemStatus}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="col-span-3">
|
||||||
|
<SelectValue placeholder="Select status" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectLabel>Status</SelectLabel>
|
||||||
|
<SelectItem value="in stock">In Stock</SelectItem>
|
||||||
|
<SelectItem value="low stock">Low Stock</SelectItem>
|
||||||
|
<SelectItem value="out of stock">Out Of Stock</SelectItem>
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
<Label htmlFor="category" className="text-right">
|
<Label htmlFor="category" className="text-right">
|
||||||
Category
|
Category
|
||||||
@ -152,33 +191,6 @@ export function EditInventoryItem() {
|
|||||||
onChange={(e) => setItemUnit(e.target.value)}
|
onChange={(e) => setItemUnit(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
|
||||||
<Label htmlFor="date" className="text-right">
|
|
||||||
Date
|
|
||||||
</Label>
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant={"outline"}
|
|
||||||
className={cn(
|
|
||||||
"col-span-3 justify-start text-left font-normal",
|
|
||||||
!date && "text-muted-foreground",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
|
||||||
{date ? format(date, "PPP") : "Pick a date"}
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-auto p-0">
|
|
||||||
<Calendar
|
|
||||||
mode="single"
|
|
||||||
selected={date}
|
|
||||||
onSelect={setDate}
|
|
||||||
initialFocus
|
|
||||||
/>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button type="submit" onClick={handleEdit}>
|
<Button type="submit" onClick={handleEdit}>
|
||||||
|
|||||||
@ -1,13 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import {
|
import { useState, useMemo } from "react";
|
||||||
useState,
|
|
||||||
JSXElementConstructor,
|
|
||||||
ReactElement,
|
|
||||||
ReactNode,
|
|
||||||
ReactPortal,
|
|
||||||
useMemo,
|
|
||||||
} from "react";
|
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import {
|
import {
|
||||||
useReactTable,
|
useReactTable,
|
||||||
@ -38,9 +31,12 @@ import { Search } from "lucide-react";
|
|||||||
import { FaSort, FaSortUp, FaSortDown } from "react-icons/fa";
|
import { FaSort, FaSortUp, FaSortDown } from "react-icons/fa";
|
||||||
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { fetchInventoryItems } from "@/api/inventory";
|
import { fetchInventoryItems, fetchInventoryStatus } from "@/api/inventory";
|
||||||
import { AddInventoryItem } from "./add-inventory-item";
|
import { AddInventoryItem } from "./add-inventory-item";
|
||||||
import { EditInventoryItem } from "./edit-inventory-item";
|
import {
|
||||||
|
EditInventoryItem,
|
||||||
|
EditInventoryItemProps,
|
||||||
|
} from "./edit-inventory-item";
|
||||||
import { DeleteInventoryItem } from "./delete-inventory-item";
|
import { DeleteInventoryItem } from "./delete-inventory-item";
|
||||||
|
|
||||||
export default function InventoryPage() {
|
export default function InventoryPage() {
|
||||||
@ -52,26 +48,42 @@ export default function InventoryPage() {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
data: inventoryItems = [],
|
data: inventoryItems = [],
|
||||||
isLoading,
|
isLoading: isItemLoading,
|
||||||
isError,
|
isError: isItemError,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: ["inventoryItems"],
|
queryKey: ["inventoryItems"],
|
||||||
queryFn: fetchInventoryItems,
|
queryFn: fetchInventoryItems,
|
||||||
staleTime: 60 * 1000,
|
staleTime: 60 * 1000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: inventoryStatus = [],
|
||||||
|
isLoading: isLoadingStatus,
|
||||||
|
isError: isErrorStatus,
|
||||||
|
} = useQuery({
|
||||||
|
queryKey: ["inventoryStatus"],
|
||||||
|
queryFn: fetchInventoryStatus,
|
||||||
|
staleTime: 60 * 1000,
|
||||||
|
});
|
||||||
// console.table(inventoryItems);
|
// console.table(inventoryItems);
|
||||||
|
console.table(inventoryStatus);
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const filteredItems = useMemo(() => {
|
const filteredItems = useMemo(() => {
|
||||||
return inventoryItems.filter((item) =>
|
return inventoryItems
|
||||||
item.name.toLowerCase().includes(searchTerm.toLowerCase())
|
.map((item) => ({
|
||||||
);
|
...item,
|
||||||
|
id: String(item.id), // Convert `id` to string here
|
||||||
|
}))
|
||||||
|
.filter((item) =>
|
||||||
|
item.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
|
);
|
||||||
}, [inventoryItems, searchTerm]);
|
}, [inventoryItems, searchTerm]);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ accessorKey: "name", header: "Name" },
|
{ accessorKey: "name", header: "Name" },
|
||||||
{ accessorKey: "category", header: "Category" },
|
{ accessorKey: "category", header: "Category" },
|
||||||
{ accessorKey: "type", header: "Type" },
|
|
||||||
{ accessorKey: "quantity", header: "Quantity" },
|
{ accessorKey: "quantity", header: "Quantity" },
|
||||||
|
{ accessorKey: "unit", header: "Unit" },
|
||||||
{ accessorKey: "lastUpdated", header: "Last Updated" },
|
{ accessorKey: "lastUpdated", header: "Last Updated" },
|
||||||
{
|
{
|
||||||
accessorKey: "status",
|
accessorKey: "status",
|
||||||
@ -83,7 +95,7 @@ export default function InventoryPage() {
|
|||||||
|
|
||||||
if (status === "Low Stock") {
|
if (status === "Low Stock") {
|
||||||
statusClass = "bg-yellow-300"; // yellow for low stock
|
statusClass = "bg-yellow-300"; // yellow for low stock
|
||||||
} else if (status === "Out of Stock") {
|
} else if (status === "Out Of Stock") {
|
||||||
statusClass = "bg-red-500 text-white"; // red for out of stock
|
statusClass = "bg-red-500 text-white"; // red for out of stock
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +109,9 @@ export default function InventoryPage() {
|
|||||||
{
|
{
|
||||||
accessorKey: "edit",
|
accessorKey: "edit",
|
||||||
header: "Edit",
|
header: "Edit",
|
||||||
cell: () => <EditInventoryItem />,
|
cell: ({ row }: { row: { original: EditInventoryItemProps } }) => (
|
||||||
|
<EditInventoryItem {...row.original} />
|
||||||
|
),
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -119,13 +133,13 @@ export default function InventoryPage() {
|
|||||||
onPaginationChange: setPagination,
|
onPaginationChange: setPagination,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isLoading)
|
if (isItemLoading || isLoadingStatus)
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center">
|
<div className="flex min-h-screen items-center justify-center">
|
||||||
Loading...
|
Loading...
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
if (isError)
|
if (isItemError || isErrorStatus)
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center">
|
<div className="flex min-h-screen items-center justify-center">
|
||||||
Error loading inventory data.
|
Error loading inventory data.
|
||||||
@ -183,7 +197,7 @@ export default function InventoryPage() {
|
|||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{table.getRowModel().rows.map((row) => (
|
{table.getRowModel().rows.map((row) => (
|
||||||
<TableRow key={row.id}>
|
<TableRow key={row.id} className="even:bg-gray-800">
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell) => (
|
||||||
<TableCell key={cell.id}>
|
<TableCell key={cell.id}>
|
||||||
{flexRender(
|
{flexRender(
|
||||||
|
|||||||
@ -120,6 +120,10 @@ export type InventoryItem = {
|
|||||||
lastUpdated: string;
|
lastUpdated: string;
|
||||||
status: string;
|
status: string;
|
||||||
};
|
};
|
||||||
|
export type InventoryItemStatus = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type CreateInventoryItemInput = Omit<InventoryItem, "id" | "lastUpdated" | "status">;
|
export type CreateInventoryItemInput = Omit<InventoryItem, "id" | "lastUpdated" | "status">;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user