diff --git a/backend/go.sum b/backend/go.sum index 9dfe70c..7650162 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -61,11 +61,13 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -75,6 +77,8 @@ github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/docs/docs/quick-start.md b/docs/docs/quick-start.md index d3e97a2..8f25644 100644 --- a/docs/docs/quick-start.md +++ b/docs/docs/quick-start.md @@ -13,7 +13,7 @@ docker run --name=homebox \ ## Docker-Compose -```yml +```yaml version: "3.4" services: diff --git a/docs/docs/tips-tricks.md b/docs/docs/tips-tricks.md new file mode 100644 index 0000000..30f9f1c --- /dev/null +++ b/docs/docs/tips-tricks.md @@ -0,0 +1,16 @@ +# Tips and Tricks + +## Custom Fields + +Custom fields are a great way to add any extra information to your item. The following types are supported: + +- [x] Text +- [ ] Integer (Future) +- [ ] Boolean (Future) +- [ ] Timestamp (Future) + +Custom fields are appended to the main details section of your item. + +!!! tip + Homebox Custom Fields also have special support for URLs. Provide a URL (`https://google.com`) and it will be automatically converted to a clickable link in the UI. Optionally, you can also use markdown syntax to add a custom text to the button. `[Google](https://google.com)` + diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 3437089..6d0924e 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -47,5 +47,6 @@ markdown_extensions: nav: - Home: index.md - Quick Start: quick-start.md + - Tips and Tricks: tips-tricks.md - Importing Data: import-csv.md - Building The Binary: build.md diff --git a/frontend/components/global/DetailsSection/DetailsSection.vue b/frontend/components/global/DetailsSection/DetailsSection.vue index 6b083e3..fc6b240 100644 --- a/frontend/components/global/DetailsSection/DetailsSection.vue +++ b/frontend/components/global/DetailsSection/DetailsSection.vue @@ -9,6 +9,14 @@ + diff --git a/frontend/components/global/DetailsSection/types.ts b/frontend/components/global/DetailsSection/types.ts index 6d2f702..a7e05d5 100644 --- a/frontend/components/global/DetailsSection/types.ts +++ b/frontend/components/global/DetailsSection/types.ts @@ -15,7 +15,13 @@ type CurrencyDetail = BaseDetail & { text: string; }; -export type CustomDetail = DateDetail | CurrencyDetail; +type LinkDetail = BaseDetail & { + type: "link"; + text: string; + href: string; +}; + +export type CustomDetail = DateDetail | CurrencyDetail | LinkDetail; export type Detail = BaseDetail & { text: StringLike; diff --git a/frontend/composables/utils.test.ts b/frontend/composables/utils.test.ts new file mode 100644 index 0000000..2b839d5 --- /dev/null +++ b/frontend/composables/utils.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, test } from "vitest"; +import { maybeUrl } from "./utils"; + +describe("maybeURL works as expected", () => { + test("basic valid URL case", () => { + const result = maybeUrl("https://example.com"); + expect(result.isUrl).toBe(true); + expect(result.url).toBe("https://example.com"); + expect(result.text).toBe("Link"); + }); + + test("special URL syntax", () => { + const result = maybeUrl("[My Text](http://example.com)"); + expect(result.isUrl).toBe(true); + expect(result.url).toBe("http://example.com"); + expect(result.text).toBe("My Text"); + }); + + test("not a url", () => { + const result = maybeUrl("not a url"); + expect(result.isUrl).toBe(false); + expect(result.url).toBe(""); + expect(result.text).toBe(""); + }); + + test("malformed special syntax", () => { + const result = maybeUrl("[My Text(http://example.com)"); + expect(result.isUrl).toBe(false); + expect(result.url).toBe(""); + expect(result.text).toBe(""); + }); +}); diff --git a/frontend/composables/utils.ts b/frontend/composables/utils.ts index 53b215d..e948349 100644 --- a/frontend/composables/utils.ts +++ b/frontend/composables/utils.ts @@ -33,3 +33,35 @@ export function fmtCurrency(value: number | string, currency = "USD", locale = " }); return formatter.format(value); } + +export type MaybeUrlResult = { + isUrl: boolean; + url: string; + text: string; +}; + +export function maybeUrl(str: string): MaybeUrlResult { + const result: MaybeUrlResult = { + isUrl: str.startsWith("http://") || str.startsWith("https://"), + url: "", + text: "", + }; + + if (!result.isUrl && !str.startsWith("[")) { + return result; + } + + if (str.startsWith("[")) { + const match = str.match(/\[(.*)\]\((.*)\)/); + if (match && match.length === 3) { + result.isUrl = true; + result.text = match[1]; + result.url = match[2]; + } + } else { + result.url = str; + result.text = "Link"; + } + + return result; +} diff --git a/frontend/pages/item/[id]/index.vue b/frontend/pages/item/[id]/index.vue index 469a9bd..9a553fd 100644 --- a/frontend/pages/item/[id]/index.vue +++ b/frontend/pages/item/[id]/index.vue @@ -96,10 +96,25 @@ name: "Notes", text: item.value?.notes, }, - ...item.value.fields.map(field => ({ - name: field.name, - text: field.textValue, - })), + ...item.value.fields.map(field => { + /** + * Support Special URL Syntax + */ + const url = maybeUrl(field.textValue); + if (url.isUrl) { + return { + name: field.name, + text: url.text, + type: "link", + href: url.url, + }; + } + + return { + name: field.name, + text: field.textValue, + }; + }), ]; });