mirror of
https://github.com/hay-kot/homebox.git
synced 2025-08-02 15:50:27 +00:00
endpoint for fullpath of an item
This commit is contained in:
parent
5fe5fac4d4
commit
102ee5afc3
11 changed files with 347 additions and 36 deletions
|
@ -93,6 +93,48 @@ func (ctrl *V1Controller) HandleItemsGetAll() errchain.HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandleItemFullPath godoc
|
||||||
|
//
|
||||||
|
// @Summary Get the full path of an item
|
||||||
|
// @Tags Items
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path string true "Item ID"
|
||||||
|
// @Success 200 {object} []repo.ItemPath
|
||||||
|
// @Router /v1/items/{id}/path [GET]
|
||||||
|
// @Security Bearer
|
||||||
|
func (ctrl *V1Controller) HandleItemFullPath() errchain.HandlerFunc {
|
||||||
|
fn := func(r *http.Request, ID uuid.UUID) ([]repo.ItemPath, error) {
|
||||||
|
auth := services.NewContext(r.Context())
|
||||||
|
item, err := ctrl.repo.Items.GetOneByGroup(auth, auth.GID, ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
paths, err := ctrl.repo.Locations.PathForLoc(auth, auth.GID, item.Location.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if item.Parent != nil {
|
||||||
|
paths = append(paths, repo.ItemPath{
|
||||||
|
Type: repo.ItemTypeItem,
|
||||||
|
ID: item.Parent.ID,
|
||||||
|
Name: item.Parent.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
paths = append(paths, repo.ItemPath{
|
||||||
|
Type: repo.ItemTypeItem,
|
||||||
|
ID: item.ID,
|
||||||
|
Name: item.Name,
|
||||||
|
})
|
||||||
|
|
||||||
|
return paths, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return adapters.CommandID("id", fn, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
// HandleItemsCreate godoc
|
// HandleItemsCreate godoc
|
||||||
//
|
//
|
||||||
// @Summary Create Item
|
// @Summary Create Item
|
||||||
|
|
|
@ -122,6 +122,7 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
|
||||||
r.Get(v1Base("/items/fields/values"), chain.ToHandlerFunc(v1Ctrl.HandleGetAllCustomFieldValues(), userMW...))
|
r.Get(v1Base("/items/fields/values"), chain.ToHandlerFunc(v1Ctrl.HandleGetAllCustomFieldValues(), userMW...))
|
||||||
|
|
||||||
r.Get(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemGet(), userMW...))
|
r.Get(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemGet(), userMW...))
|
||||||
|
r.Get(v1Base("/items/{id}/path"), chain.ToHandlerFunc(v1Ctrl.HandleItemFullPath(), userMW...))
|
||||||
r.Put(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemUpdate(), userMW...))
|
r.Put(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemUpdate(), userMW...))
|
||||||
r.Patch(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemPatch(), userMW...))
|
r.Patch(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemPatch(), userMW...))
|
||||||
r.Delete(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemDelete(), userMW...))
|
r.Delete(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemDelete(), userMW...))
|
||||||
|
|
|
@ -1017,6 +1017,42 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/v1/items/{id}/path": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Items"
|
||||||
|
],
|
||||||
|
"summary": "Get the full path of an item",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Item ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/repo.ItemPath"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/v1/labels": {
|
"/v1/labels": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
@ -2168,6 +2204,20 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"repo.ItemPath": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/definitions/repo.ItemType"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"repo.ItemSummary": {
|
"repo.ItemSummary": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -2220,6 +2270,17 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"repo.ItemType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"location",
|
||||||
|
"item"
|
||||||
|
],
|
||||||
|
"x-enum-varnames": [
|
||||||
|
"ItemTypeLocation",
|
||||||
|
"ItemTypeItem"
|
||||||
|
]
|
||||||
|
},
|
||||||
"repo.ItemUpdate": {
|
"repo.ItemUpdate": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
@ -1010,6 +1010,42 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/v1/items/{id}/path": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Items"
|
||||||
|
],
|
||||||
|
"summary": "Get the full path of an item",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Item ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/repo.ItemPath"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/v1/labels": {
|
"/v1/labels": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
@ -2161,6 +2197,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"repo.ItemPath": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/definitions/repo.ItemType"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"repo.ItemSummary": {
|
"repo.ItemSummary": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -2213,6 +2263,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"repo.ItemType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"location",
|
||||||
|
"item"
|
||||||
|
],
|
||||||
|
"x-enum-varnames": [
|
||||||
|
"ItemTypeLocation",
|
||||||
|
"ItemTypeItem"
|
||||||
|
]
|
||||||
|
},
|
||||||
"repo.ItemUpdate": {
|
"repo.ItemUpdate": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
@ -206,6 +206,15 @@ definitions:
|
||||||
x-nullable: true
|
x-nullable: true
|
||||||
x-omitempty: true
|
x-omitempty: true
|
||||||
type: object
|
type: object
|
||||||
|
repo.ItemPath:
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
$ref: '#/definitions/repo.ItemType'
|
||||||
|
type: object
|
||||||
repo.ItemSummary:
|
repo.ItemSummary:
|
||||||
properties:
|
properties:
|
||||||
archived:
|
archived:
|
||||||
|
@ -240,6 +249,14 @@ definitions:
|
||||||
updatedAt:
|
updatedAt:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
repo.ItemType:
|
||||||
|
enum:
|
||||||
|
- location
|
||||||
|
- item
|
||||||
|
type: string
|
||||||
|
x-enum-varnames:
|
||||||
|
- ItemTypeLocation
|
||||||
|
- ItemTypeItem
|
||||||
repo.ItemUpdate:
|
repo.ItemUpdate:
|
||||||
properties:
|
properties:
|
||||||
archived:
|
archived:
|
||||||
|
@ -1264,6 +1281,28 @@ paths:
|
||||||
summary: Update Maintenance Entry
|
summary: Update Maintenance Entry
|
||||||
tags:
|
tags:
|
||||||
- Maintenance
|
- Maintenance
|
||||||
|
/v1/items/{id}/path:
|
||||||
|
get:
|
||||||
|
parameters:
|
||||||
|
- description: Item ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/repo.ItemPath'
|
||||||
|
type: array
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Get the full path of an item
|
||||||
|
tags:
|
||||||
|
- Items
|
||||||
/v1/items/export:
|
/v1/items/export:
|
||||||
get:
|
get:
|
||||||
responses:
|
responses:
|
||||||
|
|
|
@ -84,7 +84,7 @@ func (csf LocationString) String() string {
|
||||||
return strings.Join(csf, " / ")
|
return strings.Join(csf, " / ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func fromPathSlice(s []repo.LocationPath) LocationString {
|
func fromPathSlice(s []repo.ItemPath) LocationString {
|
||||||
v := make(LocationString, len(s))
|
v := make(LocationString, len(s))
|
||||||
|
|
||||||
for i := range s {
|
for i := range s {
|
||||||
|
|
|
@ -260,12 +260,20 @@ type TreeQuery struct {
|
||||||
WithItems bool `json:"withItems" schema:"withItems"`
|
WithItems bool `json:"withItems" schema:"withItems"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LocationPath struct {
|
type ItemType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ItemTypeLocation ItemType = "location"
|
||||||
|
ItemTypeItem ItemType = "item"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ItemPath struct {
|
||||||
|
Type ItemType `json:"type"`
|
||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *LocationRepository) PathForLoc(ctx context.Context, GID, locID uuid.UUID) ([]LocationPath, error) {
|
func (r *LocationRepository) PathForLoc(ctx context.Context, GID, locID uuid.UUID) ([]ItemPath, error) {
|
||||||
query := `WITH RECURSIVE location_path AS (
|
query := `WITH RECURSIVE location_path AS (
|
||||||
SELECT id, name, location_children
|
SELECT id, name, location_children
|
||||||
FROM locations
|
FROM locations
|
||||||
|
@ -288,10 +296,11 @@ func (r *LocationRepository) PathForLoc(ctx context.Context, GID, locID uuid.UUI
|
||||||
}
|
}
|
||||||
defer func() { _ = rows.Close() }()
|
defer func() { _ = rows.Close() }()
|
||||||
|
|
||||||
var locations []LocationPath
|
var locations []ItemPath
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var location LocationPath
|
var location ItemPath
|
||||||
|
location.Type = ItemTypeLocation
|
||||||
if err := rows.Scan(&location.ID, &location.Name); err != nil {
|
if err := rows.Scan(&location.ID, &location.Name); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1010,6 +1010,42 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/v1/items/{id}/path": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Items"
|
||||||
|
],
|
||||||
|
"summary": "Get the full path of an item",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Item ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/repo.ItemPath"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/v1/labels": {
|
"/v1/labels": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
@ -2161,6 +2197,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"repo.ItemPath": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/definitions/repo.ItemType"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"repo.ItemSummary": {
|
"repo.ItemSummary": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -2213,6 +2263,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"repo.ItemType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"location",
|
||||||
|
"item"
|
||||||
|
],
|
||||||
|
"x-enum-varnames": [
|
||||||
|
"ItemTypeLocation",
|
||||||
|
"ItemTypeItem"
|
||||||
|
]
|
||||||
|
},
|
||||||
"repo.ItemUpdate": {
|
"repo.ItemUpdate": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
ItemCreate,
|
ItemCreate,
|
||||||
ItemOut,
|
ItemOut,
|
||||||
ItemPatch,
|
ItemPatch,
|
||||||
|
ItemPath,
|
||||||
ItemSummary,
|
ItemSummary,
|
||||||
ItemUpdate,
|
ItemUpdate,
|
||||||
MaintenanceEntry,
|
MaintenanceEntry,
|
||||||
|
@ -105,6 +106,10 @@ export class ItemsApi extends BaseAPI {
|
||||||
this.maintenance = new MaintenanceAPI(http);
|
this.maintenance = new MaintenanceAPI(http);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fullpath(id: string) {
|
||||||
|
return this.http.get<ItemPath[]>({ url: route(`/items/${id}/path`) });
|
||||||
|
}
|
||||||
|
|
||||||
getAll(q: ItemsQuery = {}) {
|
getAll(q: ItemsQuery = {}) {
|
||||||
return this.http.get<PaginationResult<ItemSummary>>({ url: route("/items", q) });
|
return this.http.get<PaginationResult<ItemSummary>>({ url: route("/items", q) });
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,12 @@ export interface ItemPatch {
|
||||||
quantity?: number | null;
|
quantity?: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ItemPath {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: ItemType;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ItemSummary {
|
export interface ItemSummary {
|
||||||
archived: boolean;
|
archived: boolean;
|
||||||
createdAt: Date | string;
|
createdAt: Date | string;
|
||||||
|
@ -145,6 +151,11 @@ export interface ItemSummary {
|
||||||
updatedAt: Date | string;
|
updatedAt: Date | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ItemType {
|
||||||
|
ItemTypeLocation = "location",
|
||||||
|
ItemTypeItem = "item",
|
||||||
|
}
|
||||||
|
|
||||||
export interface ItemUpdate {
|
export interface ItemUpdate {
|
||||||
archived: boolean;
|
archived: boolean;
|
||||||
assetId: string;
|
assetId: string;
|
||||||
|
|
|
@ -146,11 +146,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const ret: Details = [
|
const ret: Details = [
|
||||||
{
|
|
||||||
name: "Description",
|
|
||||||
type: "markdown",
|
|
||||||
text: item.value?.description,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "Quantity",
|
name: "Quantity",
|
||||||
text: item.value?.quantity,
|
text: item.value?.quantity,
|
||||||
|
@ -405,6 +400,20 @@
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const fullpath = computedAsync(async () => {
|
||||||
|
if (!item.value) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const resp = await api.items.fullpath(item.value.id);
|
||||||
|
if (resp.error) {
|
||||||
|
toast.error("Failed to load item");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.data;
|
||||||
|
});
|
||||||
|
|
||||||
const items = computedAsync(async () => {
|
const items = computedAsync(async () => {
|
||||||
if (!item.value) {
|
if (!item.value) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -442,33 +451,45 @@
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<BaseSectionHeader>
|
<div class="bg-base-100 rounded p-3">
|
||||||
<Icon name="mdi-package-variant" class="mr-2 -mt-1 text-base-content" />
|
<header class="mb-2">
|
||||||
<span class="text-base-content">
|
<div class="flex flex-wrap items-end gap-2">
|
||||||
{{ item ? item.name : "" }}
|
<div class="avatar placeholder mb-auto">
|
||||||
</span>
|
<div class="bg-neutral-focus text-neutral-content rounded-full w-12">
|
||||||
|
<Icon name="mdi-package-variant" class="h-7 w-7" />
|
||||||
<div v-if="item.parent" class="text-sm breadcrumbs pb-0">
|
</div>
|
||||||
<ul class="text-base-content/70">
|
</div>
|
||||||
<li>
|
<div>
|
||||||
<NuxtLink :to="`/item/${item.parent.id}`"> {{ item.parent.name }}</NuxtLink>
|
<div v-if="fullpath && fullpath.length > 0" class="text-sm breadcrumbs pt-0 pb-0">
|
||||||
</li>
|
<ul class="text-base-content/70">
|
||||||
<li>{{ item.name }}</li>
|
<li v-for="part in fullpath" :key="part.id">
|
||||||
</ul>
|
<NuxtLink :to="`/${part.type}/${part.id}`"> {{ part.name }}</NuxtLink>
|
||||||
</div>
|
</li>
|
||||||
<template #description>
|
</ul>
|
||||||
<Markdown :source="item.description"> </Markdown>
|
</div>
|
||||||
<div class="flex flex-wrap gap-2 mt-3">
|
<h1 class="text-2xl pb-1">
|
||||||
<NuxtLink v-if="item.location" ref="badge" class="badge p-3" :to="`/location/${item.location.id}`">
|
{{ item ? item.name : "" }}
|
||||||
<Icon name="heroicons-map-pin" class="mr-2 swap-on"></Icon>
|
</h1>
|
||||||
{{ item.location.name }}
|
<div class="flex gap-1 flex-wrap text-xs">
|
||||||
</NuxtLink>
|
<div>
|
||||||
<template v-if="item.labels && item.labels.length > 0">
|
Created
|
||||||
<LabelChip v-for="label in item.labels" :key="label.id" class="badge-primary" :label="label" />
|
<DateTime :date="item?.createdAt" />
|
||||||
</template>
|
</div>
|
||||||
|
-
|
||||||
|
<div>
|
||||||
|
Updated
|
||||||
|
<DateTime :date="item?.updatedAt" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</header>
|
||||||
</BaseSectionHeader>
|
<div class="divider my-0 mb-1"></div>
|
||||||
|
<div class="p-1 prose max-w-[100%]">
|
||||||
|
<Markdown v-if="item && item.description" class="text-base" :source="item.description"> </Markdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center justify-between mb-6 mt-3">
|
<div class="flex flex-wrap items-center justify-between mb-6 mt-3">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue