mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-31 01:57:26 +01:00
Compare commits
2 Commits
mk/keyless
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1fc4e75d7 | ||
|
|
a9ba2bc4aa |
@@ -29,6 +29,6 @@
|
||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "node",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/go:1": "1.24"
|
||||
"ghcr.io/devcontainers/features/go:1": "1.21"
|
||||
}
|
||||
}
|
||||
|
||||
40
.github/AGENTS.md
vendored
40
.github/AGENTS.md
vendored
@@ -1,40 +0,0 @@
|
||||
This is a Go based repository with a VueJS client for the frontend built with Vite and Nuxt, with ShadCN.
|
||||
|
||||
To make life easier, the use of a Taskfile is included for the majority of development commands.
|
||||
|
||||
Please follow these guidelines when contributing:
|
||||
|
||||
## Required Before Each Commit
|
||||
- Generate Swagger Files: `task swag --force`
|
||||
- Generate JS API Client: `task typescript-types --force`
|
||||
- Lint Golang: `task go:lint`
|
||||
- Lint frontend: `task ui:fix`
|
||||
|
||||
## Repository Structure
|
||||
### Backend
|
||||
- `backend/`: Contains the backend folders
|
||||
- `backend/app`: Contains main app code including API endpoints
|
||||
- `backend/internal/core`: Contains basic services such as currencies
|
||||
- `backend/data`: Contains all information related to data, including `ent` schemas, repos, migrations, etc.
|
||||
- `backend/data/migrations`: Contains migration data, the `sqlite3` sub-folder contains sqlite migrations, `postgres` sub-folder the postgres migrations, BOTH are REQUIRED.
|
||||
- `backend/data/ent/schema`: Contains the actual `ent` data models.
|
||||
- `backend/data/repo`: Contains the data repositories
|
||||
- `backend/pkgs`: Contains general helper functions and services
|
||||
|
||||
### Frontend
|
||||
- `frontend/`: Contains initial frontend files
|
||||
- `frontend/components`: Contains the ShadCN components
|
||||
- `frontend/locales`: Contains the i18n JSON for languages
|
||||
- `frontend/pages`: Contains VueJS pages
|
||||
- `frontend/test`: Contains Playwright setup
|
||||
- `frontend/test/e2e`: Contains actual Playwright test files
|
||||
|
||||
### Docs
|
||||
- `docs/`: Contains VitePress based documentation
|
||||
|
||||
## Key Guidelines
|
||||
1. Follow best practices for the various programming languages
|
||||
2. Maintain existing code structure and organization when possible
|
||||
3. Use dependency injection when reasonable
|
||||
4. Write tests for new functionality and after fixing bugs to validate they're fixed
|
||||
5. Document changes to the `docs/` folder when appropriate
|
||||
21
.github/workflows/binaries-publish.yaml
vendored
21
.github/workflows/binaries-publish.yaml
vendored
@@ -1,7 +1,6 @@
|
||||
name: Publish Release Binaries
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags: [ 'v*.*.*' ]
|
||||
|
||||
@@ -9,10 +8,6 @@ jobs:
|
||||
goreleaser:
|
||||
name: goreleaser
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -42,7 +37,6 @@ jobs:
|
||||
go install github.com/sigstore/cosign/cmd/cosign@latest
|
||||
|
||||
- name: Run GoReleaser
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
workdir: "backend"
|
||||
@@ -51,18 +45,3 @@ jobs:
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
COSIGN_PWD: ${{ secrets.COSIGN_PWD }}
|
||||
COSIGN_YES: "true"
|
||||
|
||||
- name: Run GoReleaser No Release
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
workdir: "backend"
|
||||
distribution: goreleaser
|
||||
version: "~> v2"
|
||||
args: release --clean --snapshot --skip=publish
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
COSIGN_PWD: ${{ secrets.COSIGN_PWD }}
|
||||
COSIGN_YES: "true"
|
||||
52
.github/workflows/copilot-setup-steps.yml
vendored
52
.github/workflows/copilot-setup-steps.yml
vendored
@@ -1,52 +0,0 @@
|
||||
name: "Copilot Setup Steps"
|
||||
|
||||
# Automatically run the setup steps when they are changed to allow for easy validation, and
|
||||
# allow manual testing through the repository's "Actions" tab
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- .github/workflows/copilot-setup-steps.yml
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/copilot-setup-steps.yml
|
||||
|
||||
jobs:
|
||||
# The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot.
|
||||
copilot-setup-steps:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Set the permissions to the lowest permissions possible needed for your steps.
|
||||
# Copilot will be given its own token for its operations.
|
||||
permissions:
|
||||
# If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete.
|
||||
contents: read
|
||||
|
||||
# You can define any steps you want, and they will run before the agent starts.
|
||||
# If you do not check out your code, Copilot will do this for you.
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "22"
|
||||
|
||||
- uses: pnpm/action-setup@v3.0.0
|
||||
with:
|
||||
version: 9.12.2
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
cache-dependency-path: backend/go.mod
|
||||
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Perform setup
|
||||
run: task setup
|
||||
208
.github/workflows/docker-publish-hardened.yaml
vendored
208
.github/workflows/docker-publish-hardened.yaml
vendored
@@ -1,208 +0,0 @@
|
||||
name: Docker publish hardened
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '00 0 * * *'
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'frontend/**'
|
||||
- 'Dockerfile.hardened'
|
||||
- '.dockerignore'
|
||||
- '.github/workflows/docker-publish-hardened.yaml'
|
||||
tags: [ 'v*.*.*' ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'frontend/**'
|
||||
- 'Dockerfile.hardened'
|
||||
- '.dockerignore'
|
||||
- '.github/workflows/docker-publish-hardened.yaml'
|
||||
|
||||
permissions:
|
||||
contents: read # Access to repository contents
|
||||
packages: write # Write access for pushing to GHCR
|
||||
id-token: write # Required for OIDC authentication (if used)
|
||||
attestations: write # Required for signing and attestation (if needed)
|
||||
|
||||
env:
|
||||
DOCKERHUB_REPO: sysadminsmedia/homebox
|
||||
GHCR_REPO: ghcr.io/${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
- linux/arm/v7
|
||||
|
||||
steps:
|
||||
- name: Enable Debug Logs
|
||||
run: echo "##[debug]Enabling debug logging"
|
||||
env:
|
||||
ACTIONS_RUNNER_DEBUG: true
|
||||
ACTIONS_STEP_DEBUG: true
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
echo "BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_ENV
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
branch=${{ github.event.pull_request.number || github.ref_name }}
|
||||
echo "BRANCH=${branch//\//-}" >> $GITHUB_ENV
|
||||
echo "DOCKERNAMES=${{ env.DOCKERHUB_REPO }},${{ env.GHCR_REPO }}" >> $GITHUB_ENV
|
||||
if [[ "${{ github.event_name }}" != "schedule" ]] || [[ "${{ github.ref }}" != refs/tags/* ]]; then
|
||||
echo "DOCKERNAMES=${{ env.GHCR_REPO }}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f
|
||||
with:
|
||||
images: |
|
||||
name=${{ env.DOCKERHUB_REPO }},enable=${{ github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/') }}
|
||||
name=${{ env.GHCR_REPO }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/'))
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392
|
||||
with:
|
||||
image: ghcr.io/amitie10g/binfmt:latest
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
|
||||
with:
|
||||
driver-opts: |
|
||||
image=ghcr.io/amitie10g/buildkit:master
|
||||
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83
|
||||
with:
|
||||
context: . # Explicitly specify the build context
|
||||
file: ./Dockerfile.hardened # Explicitly specify the Dockerfile
|
||||
platforms: ${{ matrix.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
outputs: type=image,"name=${{ env.DOCKERNAMES }}",push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }}
|
||||
cache-from: type=registry,ref=ghcr.io/sysadminsmedia/devcache:${{ env.PLATFORM_PAIR }}-${{ env.BRANCH }}-hardened
|
||||
cache-to: type=registry,ref=ghcr.io/sysadminsmedia/devcache:${{ env.PLATFORM_PAIR }}-${{ env.BRANCH }}-hardened,mode=max,ignore-error=true
|
||||
build-args: |
|
||||
VERSION=${{ github.ref_name }}
|
||||
COMMIT=${{ github.sha }}
|
||||
BUILD_TIME=${{ env.BUILD_TIME }}
|
||||
provenance: true
|
||||
sbom: true
|
||||
annotations: ${{ steps.meta.outputs.annotations }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
merge:
|
||||
if: github.event_name != 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
needs:
|
||||
- build
|
||||
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/'))
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
|
||||
with:
|
||||
driver-opts: |
|
||||
image=ghcr.io/amitie10g/buildkit:master
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f
|
||||
with:
|
||||
images: |
|
||||
name=${{ env.DOCKERHUB_REPO }},enable=${{ github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/') }}
|
||||
name=${{ env.GHCR_REPO }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=schedule,pattern=nightly
|
||||
flavor: |
|
||||
suffix=-hardened,onlatest=true
|
||||
|
||||
- name: Create manifest list and push GHCR
|
||||
id: push-ghcr
|
||||
working-directory: /tmp/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.GHCR_REPO }}@sha256:%s ' *)
|
||||
|
||||
- name: Create manifest list and push Dockerhub
|
||||
id: push-dockerhub
|
||||
working-directory: /tmp/digests
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/'))
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.DOCKERHUB_REPO }}@sha256:%s ' *)
|
||||
10
.github/workflows/docker-publish-rootless.yaml
vendored
10
.github/workflows/docker-publish-rootless.yaml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'frontend/**'
|
||||
- 'Dockerfile.rootless'
|
||||
- 'Dockerfile'
|
||||
- '.dockerignore'
|
||||
- '.github/workflows/docker-publish-rootless.yaml'
|
||||
ignore:
|
||||
@@ -19,7 +19,7 @@ on:
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'frontend/**'
|
||||
- 'Dockerfile.rootless'
|
||||
- 'Dockerfile'
|
||||
- '.dockerignore'
|
||||
- '.github/workflows/docker-publish-rootless.yaml'
|
||||
ignore:
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/'))
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/')) && secrets.DOCKER_USERNAME != ''
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
@@ -159,7 +159,7 @@ jobs:
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/'))
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/')) && secrets.DOCKER_USERNAME != ''
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
@@ -204,7 +204,7 @@ jobs:
|
||||
- name: Create manifest list and push Dockerhub
|
||||
id: push-dockerhub
|
||||
working-directory: /tmp/digests
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/'))
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/')) && secrets.DOCKER_USERNAME != ''
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.DOCKERHUB_REPO }}@sha256:%s ' *)
|
||||
|
||||
6
.github/workflows/docker-publish.yaml
vendored
6
.github/workflows/docker-publish.yaml
vendored
@@ -78,7 +78,7 @@ jobs:
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/'))
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/')) && secrets.DOCKER_USERNAME != ''
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
@@ -152,7 +152,7 @@ jobs:
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/'))
|
||||
if: secrets.DOCKER_USERNAME != ''
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
@@ -195,7 +195,7 @@ jobs:
|
||||
- name: Create manifest list and push Dockerhub
|
||||
id: push-dockerhub
|
||||
working-directory: /tmp/digests
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/'))
|
||||
if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/')) && secrets.DOCKER_USERNAME != ''
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.DOCKERHUB_REPO }}@sha256:%s ' *)
|
||||
|
||||
5
.github/workflows/pull-requests.yaml
vendored
5
.github/workflows/pull-requests.yaml
vendored
@@ -9,10 +9,7 @@ on:
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'frontend/**'
|
||||
- '.github/workflows/partial-backend.yaml'
|
||||
- '.github/workflows/partial-frontend.yaml'
|
||||
- '.github/workflows/e2e-partial.yaml'
|
||||
- '.github/workflows/pull-requests.yaml'
|
||||
- '.github/workflows/**'
|
||||
|
||||
jobs:
|
||||
backend-tests:
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
# ---------------------------------------
|
||||
# Node dependencies stage
|
||||
# ---------------------------------------
|
||||
FROM public.ecr.aws/docker/library/node:lts-alpine AS frontend-dependencies
|
||||
WORKDIR /app
|
||||
|
||||
# Install pnpm globally (caching layer)
|
||||
RUN npm install -g pnpm
|
||||
|
||||
# Copy package.json and lockfile to leverage caching
|
||||
COPY frontend/package.json frontend/pnpm-lock.yaml ./
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# ---------------------------------------
|
||||
# Build Nuxt (frontend) stage
|
||||
# ---------------------------------------
|
||||
FROM public.ecr.aws/docker/library/node:lts-alpine AS frontend-builder
|
||||
WORKDIR /app
|
||||
|
||||
# Install pnpm globally again (it can reuse the cache if not changed)
|
||||
RUN npm install -g pnpm
|
||||
|
||||
# Copy over source files and node_modules from dependencies stage
|
||||
COPY frontend .
|
||||
COPY --from=frontend-dependencies /app/node_modules ./node_modules
|
||||
RUN pnpm build
|
||||
|
||||
# ---------------------------------------
|
||||
# Go dependencies stage
|
||||
# ---------------------------------------
|
||||
FROM public.ecr.aws/docker/library/golang:alpine AS builder-dependencies
|
||||
WORKDIR /go/src/app
|
||||
|
||||
# Copy go.mod and go.sum for better caching
|
||||
COPY ./backend/go.mod ./backend/go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# ---------------------------------------
|
||||
# Build API + healthcheck stage
|
||||
# ---------------------------------------
|
||||
FROM public.ecr.aws/docker/library/golang:alpine AS builder
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ARG BUILD_TIME
|
||||
ARG COMMIT
|
||||
ARG VERSION
|
||||
|
||||
# Install necessary build tools
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add --no-cache git build-base gcc g++
|
||||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
# Copy Go modules (from dependencies stage) and source code
|
||||
COPY --from=builder-dependencies /go/pkg/mod /go/pkg/mod
|
||||
COPY ./backend .
|
||||
|
||||
# Clear old public files and copy new ones from frontend build
|
||||
RUN rm -rf ./app/api/public
|
||||
COPY --from=frontend-builder /app/.output/public ./app/api/static/public
|
||||
|
||||
# Use cache for Go build artifacts to build Homebox API
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build \
|
||||
-ldflags "-s -w -X main.commit=$COMMIT -X main.buildTime=$BUILD_TIME -X main.version=$VERSION" \
|
||||
-tags nodynamic -o /go/bin/api -v ./app/api/*.go
|
||||
|
||||
RUN chmod +x /go/bin/api
|
||||
RUN mkdir /app
|
||||
RUN mkdir /data
|
||||
|
||||
# ---------- Build static healthcheck helper ----------
|
||||
# A small Go program that GETs the status URL and exits 0 on 2xx.
|
||||
RUN cat > /tmp/healthcheck.go <<'EOF'
|
||||
package main
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
func main() {
|
||||
url := "http://127.0.0.1:7745/api/v1/status"
|
||||
if len(os.Args) > 1 { url = os.Args[1] }
|
||||
c := &http.Client{ Timeout: 3 * time.Second }
|
||||
resp, err := c.Get(url)
|
||||
if err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) }
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode/100 != 2 {
|
||||
fmt.Fprintln(os.Stderr, "unexpected status:", resp.StatusCode)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \
|
||||
go build -ldflags "-s -w" -o /go/bin/hc /tmp/healthcheck.go
|
||||
|
||||
# ---------------------------------------
|
||||
# Production stage
|
||||
# ---------------------------------------
|
||||
FROM gcr.io/distroless/static:nonroot
|
||||
ENV HBOX_MODE=production
|
||||
ENV HBOX_STORAGE_CONN_STRING=file:///?no_tmp_dir=true
|
||||
ENV HBOX_STORAGE_PREFIX_PATH=data
|
||||
ENV HBOX_DATABASE_SQLITE_PATH=/data/homebox.db?_pragma=busy_timeout=2000&_pragma=journal_mode=WAL&_fk=1&_time_format=sqlite
|
||||
|
||||
# Create application directory and copy over built Go binary and assets
|
||||
COPY --from=builder --chown=65532:65532 /app /app
|
||||
COPY --from=builder --chown=65532:65532 --chmod=755 /go/bin/api /app
|
||||
COPY --from=builder --chown=65532:65532 /data /data
|
||||
|
||||
# Copy the healthcheck helper
|
||||
COPY --from=builder --chown=65532:65532 --chmod=755 /go/bin/hc /app/healthcheck
|
||||
|
||||
# Labels and configuration for the final image
|
||||
LABEL Name=homebox Version=0.0.1
|
||||
LABEL org.opencontainers.image.source="https://github.com/sysadminsmedia/homebox"
|
||||
|
||||
# Expose necessary ports for Homebox
|
||||
EXPOSE 7745
|
||||
WORKDIR /app
|
||||
|
||||
# Persist volume for data
|
||||
VOLUME [ "/data" ]
|
||||
|
||||
# Entrypoint and CMD
|
||||
USER 65532
|
||||
ENTRYPOINT [ "/app/api" ]
|
||||
CMD [ "/data/config.yml" ]
|
||||
|
||||
# JSON exec-form healthcheck (no shell, no wget)
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
|
||||
CMD ["/app/healthcheck", "http://127.0.0.1:7745/api/v1/status"]
|
||||
@@ -14,7 +14,6 @@ builds:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
- freebsd
|
||||
goarch:
|
||||
- amd64
|
||||
- "386"
|
||||
@@ -26,16 +25,11 @@ builds:
|
||||
goarch: arm
|
||||
- goos: windows
|
||||
goarch: "386"
|
||||
- goos: freebsd
|
||||
goarch: arm
|
||||
- goos: freebsd
|
||||
goarch: "386"
|
||||
tags:
|
||||
- >-
|
||||
{{- if eq .Arch "riscv64" }}nodynamic
|
||||
{{- else if eq .Arch "arm" }}nodynamic
|
||||
{{- else if eq .Arch "386" }}nodynamic
|
||||
{{- else if eq .Os "freebsd" }}nodynamic
|
||||
{{ end }}
|
||||
|
||||
signs:
|
||||
@@ -43,7 +37,7 @@ signs:
|
||||
stdin: "{{ .Env.COSIGN_PWD }}"
|
||||
args:
|
||||
- "sign-blob"
|
||||
- "--output-certificate=${certificate}"
|
||||
- "--key=cosign.key"
|
||||
- "--output-signature=${signature}"
|
||||
- "${artifact}"
|
||||
- "--yes" # needed on cosign 2.0.0+
|
||||
|
||||
@@ -254,25 +254,6 @@ func (ctrl *V1Controller) HandleItemPatch() errchain.HandlerFunc {
|
||||
return adapters.ActionID("id", fn, http.StatusOK)
|
||||
}
|
||||
|
||||
// HandleItemDuplicate godocs
|
||||
//
|
||||
// @Summary Duplicate Item
|
||||
// @Tags Items
|
||||
// @Produce json
|
||||
// @Param id path string true "Item ID"
|
||||
// @Param payload body repo.DuplicateOptions true "Duplicate Options"
|
||||
// @Success 201 {object} repo.ItemOut
|
||||
// @Router /v1/items/{id}/duplicate [POST]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemDuplicate() errchain.HandlerFunc {
|
||||
fn := func(r *http.Request, ID uuid.UUID, options repo.DuplicateOptions) (repo.ItemOut, error) {
|
||||
ctx := services.NewContext(r.Context())
|
||||
return ctrl.svc.Items.Duplicate(ctx, ctx.GID, ID, options)
|
||||
}
|
||||
|
||||
return adapters.ActionID("id", fn, http.StatusCreated)
|
||||
}
|
||||
|
||||
// HandleGetAllCustomFieldNames godocs
|
||||
//
|
||||
// @Summary Get All Custom Field Names
|
||||
|
||||
@@ -205,7 +205,7 @@ func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r
|
||||
}(bucket)
|
||||
|
||||
// Set the Content-Disposition header for RFC6266 compliance
|
||||
disposition := "inline; filename*=UTF-8''" + url.QueryEscape(doc.Title)
|
||||
disposition := "attachment; filename*=UTF-8''" + url.QueryEscape(doc.Title)
|
||||
w.Header().Set("Content-Disposition", disposition)
|
||||
http.ServeContent(w, r, doc.Title, doc.CreatedAt, file)
|
||||
return nil
|
||||
|
||||
@@ -29,7 +29,7 @@ func generateOrPrint(ctrl *V1Controller, w http.ResponseWriter, r *http.Request,
|
||||
_, err = w.Write([]byte("Printed!"))
|
||||
return err
|
||||
} else {
|
||||
return labelmaker.GenerateLabel(w, ¶ms, ctrl.config)
|
||||
return labelmaker.GenerateLabel(w, ¶ms)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,332 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/hay-kot/httpkit/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/config"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/web/adapters"
|
||||
)
|
||||
|
||||
type UPCITEMDBResponse struct {
|
||||
Code string `json:"code"`
|
||||
Total int `json:"total"`
|
||||
Offset int `json:"offset"`
|
||||
Items []struct {
|
||||
Ean string `json:"ean"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Upc string `json:"upc"`
|
||||
Brand string `json:"brand"`
|
||||
Model string `json:"model"`
|
||||
Color string `json:"color"`
|
||||
Size string `json:"size"`
|
||||
Dimension string `json:"dimension"`
|
||||
Weight string `json:"weight"`
|
||||
Category string `json:"category"`
|
||||
LowestRecordedPrice float64 `json:"lowest_recorded_price"`
|
||||
HighestRecordedPrice float64 `json:"highest_recorded_price"`
|
||||
Images []string `json:"images"`
|
||||
Offers []struct {
|
||||
Merchant string `json:"merchant"`
|
||||
Domain string `json:"domain"`
|
||||
Title string `json:"title"`
|
||||
Currency string `json:"currency"`
|
||||
ListPrice string `json:"list_price"`
|
||||
Price float64 `json:"price"`
|
||||
Shipping string `json:"shipping"`
|
||||
Condition string `json:"condition"`
|
||||
Availability string `json:"availability"`
|
||||
Link string `json:"link"`
|
||||
UpdatedT int `json:"updated_t"`
|
||||
} `json:"offers"`
|
||||
Asin string `json:"asin"`
|
||||
Elid string `json:"elid"`
|
||||
} `json:"items"`
|
||||
}
|
||||
|
||||
type BARCODESPIDER_COMResponse struct {
|
||||
ItemResponse struct {
|
||||
Code int `json:"code"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
} `json:"item_response"`
|
||||
ItemAttributes struct {
|
||||
Title string `json:"title"`
|
||||
Upc string `json:"upc"`
|
||||
Ean string `json:"ean"`
|
||||
ParentCategory string `json:"parent_category"`
|
||||
Category string `json:"category"`
|
||||
Brand string `json:"brand"`
|
||||
Model string `json:"model"`
|
||||
Mpn string `json:"mpn"`
|
||||
Manufacturer string `json:"manufacturer"`
|
||||
Publisher string `json:"publisher"`
|
||||
Asin string `json:"asin"`
|
||||
Color string `json:"color"`
|
||||
Size string `json:"size"`
|
||||
Weight string `json:"weight"`
|
||||
Image string `json:"image"`
|
||||
IsAdult string `json:"is_adult"`
|
||||
Description string `json:"description"`
|
||||
} `json:"item_attributes"`
|
||||
Stores []struct {
|
||||
StoreName string `json:"store_name"`
|
||||
Title string `json:"title"`
|
||||
Image string `json:"image"`
|
||||
Price string `json:"price"`
|
||||
Currency string `json:"currency"`
|
||||
Link string `json:"link"`
|
||||
Updated string `json:"updated"`
|
||||
} `json:"Stores"`
|
||||
}
|
||||
|
||||
// HandleGenerateQRCode godoc
|
||||
//
|
||||
// @Summary Search EAN from Barcode
|
||||
// @Tags Items
|
||||
// @Produce json
|
||||
// @Param data query string false "barcode to be searched"
|
||||
// @Success 200 {object} []repo.BarcodeProduct
|
||||
// @Router /v1/products/search-from-barcode [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleProductSearchFromBarcode(conf config.BarcodeAPIConf) errchain.HandlerFunc {
|
||||
type query struct {
|
||||
// 80 characters is the longest non-2D barcode length (GS1-128)
|
||||
EAN string `schema:"productEAN" validate:"required,max=80"`
|
||||
}
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
q, err := adapters.DecodeQuery[query](r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
const TIMEOUT_SEC = 10
|
||||
|
||||
log.Info().Msg("Processing barcode lookup request on: " + q.EAN)
|
||||
|
||||
// Search on UPCITEMDB
|
||||
var products []repo.BarcodeProduct
|
||||
|
||||
// www.ean-search.org/: not free
|
||||
|
||||
// Example code: dewalt 5035048748428
|
||||
|
||||
upcitemdb := func(iEan string) ([]repo.BarcodeProduct, error) {
|
||||
client := &http.Client{Timeout: TIMEOUT_SEC * time.Second}
|
||||
resp, err := client.Get("https://api.upcitemdb.com/prod/trial/lookup?upc=" + iEan)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = errors.Join(err, resp.Body.Close())
|
||||
}()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("API returned status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// We Read the response body on the line below.
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Uncomment the following string for debug
|
||||
// sb := string(body)
|
||||
// log.Debug().Msg("Response: " + sb)
|
||||
|
||||
var result UPCITEMDBResponse
|
||||
if err := json.Unmarshal(body, &result); err != nil { // Parse []byte to go struct pointer
|
||||
log.Error().Msg("Can not unmarshal JSON")
|
||||
}
|
||||
|
||||
var res []repo.BarcodeProduct
|
||||
|
||||
for _, it := range result.Items {
|
||||
var p repo.BarcodeProduct
|
||||
p.SearchEngineName = "upcitemdb.com"
|
||||
p.Barcode = iEan
|
||||
|
||||
p.Item.Description = it.Description
|
||||
p.Item.Name = it.Title
|
||||
p.Manufacturer = it.Brand
|
||||
p.ModelNumber = it.Model
|
||||
if len(it.Images) != 0 {
|
||||
p.ImageURL = it.Images[0]
|
||||
}
|
||||
|
||||
res = append(res, p)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
ps, err := upcitemdb(q.EAN)
|
||||
if err != nil {
|
||||
log.Error().Msg("Can not retrieve product from upcitemdb.com" + err.Error())
|
||||
}
|
||||
|
||||
// Barcode spider implementation
|
||||
barcodespider := func(tokenAPI string, iEan string) ([]repo.BarcodeProduct, error) {
|
||||
if len(tokenAPI) == 0 {
|
||||
return nil, errors.New("no api token configured for barcodespider. " +
|
||||
"Please define the api token in environment variable HBOX_BARCODE_TOKEN_BARCODESPIDER")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(
|
||||
"GET", "https://api.barcodespider.com/v1/lookup?upc="+iEan, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("token", tokenAPI)
|
||||
|
||||
client := &http.Client{Timeout: TIMEOUT_SEC * time.Second}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// defer the call to Body.Close(). We also check the error code, and merge
|
||||
// it with the other error in this code to avoid error overiding.
|
||||
defer func() {
|
||||
err = errors.Join(err, resp.Body.Close())
|
||||
}()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("barcodespider API returned status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// We Read the response body on the line below.
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Uncomment the following string for debug
|
||||
// sb := string(body)
|
||||
// log.Debug().Msg("Response: " + sb)
|
||||
|
||||
var result BARCODESPIDER_COMResponse
|
||||
if err := json.Unmarshal(body, &result); err != nil { // Parse []byte to go struct pointer
|
||||
log.Error().Msg("Can not unmarshal JSON")
|
||||
}
|
||||
|
||||
// TODO: check 200 code on HTTP response.
|
||||
var p repo.BarcodeProduct
|
||||
p.Barcode = iEan
|
||||
p.SearchEngineName = "barcodespider.com"
|
||||
p.Item.Name = result.ItemAttributes.Title
|
||||
p.Item.Description = result.ItemAttributes.Description
|
||||
p.Manufacturer = result.ItemAttributes.Brand
|
||||
p.ModelNumber = result.ItemAttributes.Model
|
||||
p.ImageURL = result.ItemAttributes.Image
|
||||
|
||||
var res []repo.BarcodeProduct
|
||||
res = append(res, p)
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
ps2, err := barcodespider(conf.TokenBarcodespider, q.EAN)
|
||||
if err != nil {
|
||||
log.Error().Msg("Can not retrieve product from barcodespider.com: " + err.Error())
|
||||
}
|
||||
|
||||
// Merge everything.
|
||||
products = append(products, ps...)
|
||||
|
||||
products = append(products, ps2...)
|
||||
|
||||
// Retrieve images if possible
|
||||
for i := range products {
|
||||
p := &products[i]
|
||||
|
||||
if len(p.ImageURL) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Validate URL is HTTPS
|
||||
u, err := url.Parse(p.ImageURL)
|
||||
if err != nil || u.Scheme != "https" {
|
||||
log.Warn().Msg("Skipping non-HTTPS image URL: " + p.ImageURL)
|
||||
continue
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: TIMEOUT_SEC * time.Second}
|
||||
res, err := client.Get(p.ImageURL)
|
||||
if err != nil {
|
||||
log.Warn().Msg("Cannot fetch image for URL: " + p.ImageURL + ": " + err.Error())
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = errors.Join(err, res.Body.Close())
|
||||
}()
|
||||
|
||||
// Validate response
|
||||
if res.StatusCode != http.StatusOK {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check content type
|
||||
contentType := res.Header.Get("Content-Type")
|
||||
if !strings.HasPrefix(contentType, "image/") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Limit image size to 8MB
|
||||
limitedReader := io.LimitReader(res.Body, 8*1024*1024)
|
||||
|
||||
// Read data of image
|
||||
bytes, err := io.ReadAll(limitedReader)
|
||||
if err != nil {
|
||||
log.Warn().Msg(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// Convert to Base64
|
||||
var base64Encoding string
|
||||
|
||||
// Determine the content type of the image file
|
||||
mimeType := http.DetectContentType(bytes)
|
||||
|
||||
// Prepend the appropriate URI scheme header depending
|
||||
// on the MIME type
|
||||
switch mimeType {
|
||||
case "image/jpeg":
|
||||
base64Encoding += "data:image/jpeg;base64,"
|
||||
case "image/png":
|
||||
base64Encoding += "data:image/png;base64,"
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
// Append the base64 encoded output
|
||||
base64Encoding += base64.StdEncoding.EncodeToString(bytes)
|
||||
|
||||
p.ImageBase64 = base64Encoding
|
||||
}
|
||||
|
||||
if len(products) != 0 {
|
||||
return server.JSON(w, http.StatusOK, products)
|
||||
}
|
||||
|
||||
return server.JSON(w, http.StatusNoContent, nil)
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/sysadminsmedia/homebox/backend/pkgs/utils"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pressly/goose/v3"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/pressly/goose/v3"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/analytics"
|
||||
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/hay-kot/httpkit/graceful"
|
||||
@@ -24,15 +28,16 @@ import (
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/migrations"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/analytics"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/config"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/web/mid"
|
||||
"go.balki.me/anyhttp"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
_ "github.com/sysadminsmedia/homebox/backend/internal/data/migrations/postgres"
|
||||
_ "github.com/sysadminsmedia/homebox/backend/internal/data/migrations/sqlite3"
|
||||
_ "github.com/sysadminsmedia/homebox/backend/pkgs/cgofreesqlite"
|
||||
|
||||
"gocloud.dev/pubsub"
|
||||
_ "gocloud.dev/pubsub/awssnssqs"
|
||||
_ "gocloud.dev/pubsub/azuresb"
|
||||
_ "gocloud.dev/pubsub/gcppubsub"
|
||||
@@ -97,56 +102,81 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func run(cfg *config.Config) error {
|
||||
app := new(cfg)
|
||||
app.setupLogger()
|
||||
|
||||
if cfg.Options.AllowAnalytics {
|
||||
analytics.Send(version, build())
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Initialize Database & Repos
|
||||
err := setupStorageDir(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
if strings.HasPrefix(cfg.Storage.ConnString, "file:///./") {
|
||||
raw := strings.TrimPrefix(cfg.Storage.ConnString, "file:///./")
|
||||
clean := filepath.Clean(raw)
|
||||
absBase, err := filepath.Abs(clean)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to get absolute path for storage connection string")
|
||||
}
|
||||
// Construct and validate the full storage path
|
||||
storageDir := filepath.Join(absBase, cfg.Storage.PrefixPath)
|
||||
// Set windows paths to use forward slashes required by go-cloud
|
||||
storageDir = strings.ReplaceAll(storageDir, "\\", "/")
|
||||
if !strings.HasPrefix(storageDir, absBase+"/") && storageDir != absBase {
|
||||
log.Fatal().
|
||||
Str("path", storageDir).
|
||||
Msg("invalid storage path: you tried to use a prefix that is not a subdirectory of the base path")
|
||||
}
|
||||
// Create with more restrictive permissions
|
||||
if err := os.MkdirAll(storageDir, 0o750); err != nil {
|
||||
log.Fatal().
|
||||
Err(err).
|
||||
Msg("failed to create data directory")
|
||||
}
|
||||
}
|
||||
|
||||
if strings.ToLower(cfg.Database.Driver) == "postgres" {
|
||||
if !validatePostgresSSLMode(cfg.Database.SslMode) {
|
||||
log.Error().Str("sslmode", cfg.Database.SslMode).Msg("invalid sslmode")
|
||||
return fmt.Errorf("invalid sslmode: %s", cfg.Database.SslMode)
|
||||
log.Fatal().Str("sslmode", cfg.Database.SslMode).Msg("invalid sslmode")
|
||||
}
|
||||
}
|
||||
|
||||
databaseURL, err := setupDatabaseURL(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
// Set up the database URL based on the driver because for some reason a common URL format is not used
|
||||
databaseURL := ""
|
||||
switch strings.ToLower(cfg.Database.Driver) {
|
||||
case "sqlite3":
|
||||
databaseURL = cfg.Database.SqlitePath
|
||||
|
||||
// Create directory for SQLite database if it doesn't exist
|
||||
dbFilePath := strings.Split(cfg.Database.SqlitePath, "?")[0] // Remove query parameters
|
||||
dbDir := filepath.Dir(dbFilePath)
|
||||
if err := os.MkdirAll(dbDir, 0o755); err != nil {
|
||||
log.Fatal().Err(err).Str("path", dbDir).Msg("failed to create SQLite database directory")
|
||||
}
|
||||
case "postgres":
|
||||
databaseURL = fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", cfg.Database.Host, cfg.Database.Port, cfg.Database.Username, cfg.Database.Password, cfg.Database.Database, cfg.Database.SslMode)
|
||||
default:
|
||||
log.Fatal().Str("driver", cfg.Database.Driver).Msg("unsupported database driver")
|
||||
}
|
||||
|
||||
c, err := ent.Open(strings.ToLower(cfg.Database.Driver), databaseURL)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
log.Fatal().
|
||||
Err(err).
|
||||
Str("driver", strings.ToLower(cfg.Database.Driver)).
|
||||
Str("host", cfg.Database.Host).
|
||||
Str("port", cfg.Database.Port).
|
||||
Str("database", cfg.Database.Database).
|
||||
Msg("failed opening connection to {driver} database at {host}:{port}/{database}")
|
||||
return fmt.Errorf("failed opening connection to %s database at %s:%s/%s: %w",
|
||||
strings.ToLower(cfg.Database.Driver),
|
||||
cfg.Database.Host,
|
||||
cfg.Database.Port,
|
||||
cfg.Database.Database,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
migrationsFs, err := migrations.Migrations(strings.ToLower(cfg.Database.Driver))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get migrations for %s: %w", strings.ToLower(cfg.Database.Driver), err)
|
||||
}
|
||||
|
||||
goose.SetBaseFS(migrationsFs)
|
||||
goose.SetBaseFS(migrations.Migrations(strings.ToLower(cfg.Database.Driver)))
|
||||
err = goose.SetDialect(strings.ToLower(cfg.Database.Driver))
|
||||
if err != nil {
|
||||
log.Error().Str("driver", cfg.Database.Driver).Msg("unsupported database driver")
|
||||
log.Fatal().Str("driver", cfg.Database.Driver).Msg("unsupported database driver")
|
||||
return fmt.Errorf("unsupported database driver: %s", cfg.Database.Driver)
|
||||
}
|
||||
|
||||
@@ -156,9 +186,25 @@ func run(cfg *config.Config) error {
|
||||
return err
|
||||
}
|
||||
|
||||
collectFuncs, err := loadCurrencies(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
collectFuncs := []currencies.CollectorFunc{
|
||||
currencies.CollectDefaults(),
|
||||
}
|
||||
|
||||
if cfg.Options.CurrencyConfig != "" {
|
||||
log.Info().
|
||||
Str("path", cfg.Options.CurrencyConfig).
|
||||
Msg("loading currency config file")
|
||||
|
||||
content, err := os.ReadFile(cfg.Options.CurrencyConfig)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("path", cfg.Options.CurrencyConfig).
|
||||
Msg("failed to read currency config file")
|
||||
return err
|
||||
}
|
||||
|
||||
collectFuncs = append(collectFuncs, currencies.CollectJSON(bytes.NewReader(content)))
|
||||
}
|
||||
|
||||
currencies, err := currencies.CollectionCurrencies(collectFuncs...)
|
||||
@@ -212,52 +258,154 @@ func run(cfg *config.Config) error {
|
||||
_ = httpserver.Shutdown(context.Background())
|
||||
}()
|
||||
|
||||
listener, addrType, addrCfg, err := anyhttp.GetListener(cfg.Web.Host)
|
||||
if err == nil {
|
||||
switch addrType {
|
||||
case anyhttp.SystemdFD:
|
||||
sysdCfg := addrCfg.(*anyhttp.SysdConfig)
|
||||
if sysdCfg.IdleTimeout != nil {
|
||||
log.Error().Msg("idle timeout not yet supported. Please remove and try again")
|
||||
return errors.New("idle timeout not yet supported. Please remove and try again")
|
||||
}
|
||||
fallthrough
|
||||
case anyhttp.UnixSocket:
|
||||
log.Info().Msgf("Server is running on %s", cfg.Web.Host)
|
||||
return httpserver.Serve(listener)
|
||||
}
|
||||
} else {
|
||||
log.Debug().Msgf("anyhttp error: %v", err)
|
||||
}
|
||||
log.Info().Msgf("Server is running on %s:%s", cfg.Web.Host, cfg.Web.Port)
|
||||
return httpserver.ListenAndServe()
|
||||
})
|
||||
|
||||
// =========================================================================
|
||||
// Start Reoccurring Tasks
|
||||
registerRecurringTasks(app, cfg, runner)
|
||||
|
||||
// Send analytics if enabled at around midnight UTC
|
||||
if cfg.Options.AllowAnalytics {
|
||||
analyticsTime := time.Second
|
||||
runner.AddPlugin(NewTask("send-analytics", analyticsTime, func(ctx context.Context) {
|
||||
for {
|
||||
now := time.Now().UTC()
|
||||
nextMidnight := time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, time.UTC)
|
||||
dur := time.Until(nextMidnight)
|
||||
analyticsTime = dur
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(dur):
|
||||
log.Debug().Msg("running send analytics")
|
||||
err := analytics.Send(version, build())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to send analytics")
|
||||
}
|
||||
runner.AddFunc("eventbus", app.bus.Run)
|
||||
|
||||
runner.AddFunc("seed_database", func(ctx context.Context) error {
|
||||
// TODO: Remove through external API that does setup
|
||||
if cfg.Demo {
|
||||
log.Info().Msg("Running in demo mode, creating demo data")
|
||||
err := app.SetupDemo()
|
||||
if err != nil {
|
||||
log.Fatal().Msg(err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
runner.AddPlugin(NewTask("purge-tokens", time.Duration(24)*time.Hour, func(ctx context.Context) {
|
||||
_, err := app.repos.AuthTokens.PurgeExpiredTokens(ctx)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("failed to purge expired tokens")
|
||||
}
|
||||
}))
|
||||
|
||||
runner.AddPlugin(NewTask("purge-invitations", time.Duration(24)*time.Hour, func(ctx context.Context) {
|
||||
_, err := app.repos.Groups.InvitationPurge(ctx)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("failed to purge expired invitations")
|
||||
}
|
||||
}))
|
||||
|
||||
runner.AddPlugin(NewTask("send-notifications", time.Duration(1)*time.Hour, func(ctx context.Context) {
|
||||
now := time.Now()
|
||||
|
||||
if now.Hour() == 8 {
|
||||
fmt.Println("run notifiers")
|
||||
err := app.services.BackgroundService.SendNotifiersToday(context.Background())
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("failed to send notifiers")
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
go runner.AddFunc("create-thumbnails-subscription", func(ctx context.Context) error {
|
||||
pubsubString, err := utils.GenerateSubPubConn(cfg.Database.PubSubConnString, "thumbnails")
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to generate pubsub connection string")
|
||||
return err
|
||||
}
|
||||
topic, err := pubsub.OpenTopic(ctx, pubsubString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(topic *pubsub.Topic, ctx context.Context) {
|
||||
err := topic.Shutdown(ctx)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("fail to shutdown pubsub topic")
|
||||
}
|
||||
}(topic, ctx)
|
||||
|
||||
subscription, err := pubsub.OpenSubscription(ctx, pubsubString)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to open pubsub topic")
|
||||
return err
|
||||
}
|
||||
defer func(topic *pubsub.Subscription, ctx context.Context) {
|
||||
err := topic.Shutdown(ctx)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("fail to shutdown pubsub topic")
|
||||
}
|
||||
}(subscription, ctx)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
msg, err := subscription.Receive(ctx)
|
||||
log.Debug().Msg("received thumbnail generation request from pubsub topic")
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to receive message from pubsub topic")
|
||||
}
|
||||
groupId, err := uuid.Parse(msg.Metadata["group_id"])
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("group_id", msg.Metadata["group_id"]).
|
||||
Msg("failed to parse group ID from message metadata")
|
||||
}
|
||||
attachmentId, err := uuid.Parse(msg.Metadata["attachment_id"])
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("attachment_id", msg.Metadata["attachment_id"]).
|
||||
Msg("failed to parse attachment ID from message metadata")
|
||||
}
|
||||
err = app.repos.Attachments.CreateThumbnail(ctx, groupId, attachmentId, msg.Metadata["title"], msg.Metadata["path"])
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to create thumbnail")
|
||||
}
|
||||
msg.Ack()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if cfg.Options.GithubReleaseCheck {
|
||||
runner.AddPlugin(NewTask("get-latest-github-release", time.Hour, func(ctx context.Context) {
|
||||
log.Debug().Msg("running get latest github release")
|
||||
err := app.services.BackgroundService.GetLatestGithubRelease(context.Background())
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("failed to get latest github release")
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
if cfg.Debug.Enabled {
|
||||
runner.AddFunc("debug", func(ctx context.Context) error {
|
||||
debugserver := http.Server{
|
||||
Addr: fmt.Sprintf("%s:%s", cfg.Web.Host, cfg.Debug.Port),
|
||||
Handler: app.debugRouter(),
|
||||
ReadTimeout: cfg.Web.ReadTimeout,
|
||||
WriteTimeout: cfg.Web.WriteTimeout,
|
||||
IdleTimeout: cfg.Web.IdleTimeout,
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
_ = debugserver.Shutdown(context.Background())
|
||||
}()
|
||||
|
||||
log.Info().Msgf("Debug server is running on %s:%s", cfg.Web.Host, cfg.Debug.Port)
|
||||
return debugserver.ListenAndServe()
|
||||
})
|
||||
// Print the configuration to the console
|
||||
cfg.Print()
|
||||
}
|
||||
|
||||
return runner.Start(context.Background())
|
||||
}
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/httpkit/graceful"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/config"
|
||||
"github.com/sysadminsmedia/homebox/backend/pkgs/utils"
|
||||
"gocloud.dev/pubsub"
|
||||
)
|
||||
|
||||
func registerRecurringTasks(app *app, cfg *config.Config, runner *graceful.Runner) {
|
||||
runner.AddFunc("eventbus", app.bus.Run)
|
||||
|
||||
runner.AddFunc("seed_database", func(ctx context.Context) error {
|
||||
if cfg.Demo {
|
||||
log.Info().Msg("Running in demo mode, creating demo data")
|
||||
err := app.SetupDemo()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to setup demo data")
|
||||
return fmt.Errorf("failed to setup demo data: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
runner.AddPlugin(NewTask("purge-tokens", 24*time.Hour, func(ctx context.Context) {
|
||||
_, err := app.repos.AuthTokens.PurgeExpiredTokens(ctx)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to purge expired tokens")
|
||||
}
|
||||
}))
|
||||
|
||||
runner.AddPlugin(NewTask("purge-invitations", 24*time.Hour, func(ctx context.Context) {
|
||||
_, err := app.repos.Groups.InvitationPurge(ctx)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to purge expired invitations")
|
||||
}
|
||||
}))
|
||||
|
||||
runner.AddPlugin(NewTask("send-notifications", time.Hour, func(ctx context.Context) {
|
||||
now := time.Now()
|
||||
if now.Hour() == 8 {
|
||||
fmt.Println("run notifiers")
|
||||
err := app.services.BackgroundService.SendNotifiersToday(context.Background())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to send notifiers")
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
if cfg.Thumbnail.Enabled {
|
||||
runner.AddFunc("create-thumbnails-subscription", func(ctx context.Context) error {
|
||||
pubsubString, err := utils.GenerateSubPubConn(cfg.Database.PubSubConnString, "thumbnails")
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to generate pubsub connection string")
|
||||
return err
|
||||
}
|
||||
topic, err := pubsub.OpenTopic(ctx, pubsubString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(topic *pubsub.Topic, ctx context.Context) {
|
||||
err := topic.Shutdown(ctx)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("fail to shutdown pubsub topic")
|
||||
}
|
||||
}(topic, ctx)
|
||||
|
||||
subscription, err := pubsub.OpenSubscription(ctx, pubsubString)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to open pubsub topic")
|
||||
return err
|
||||
}
|
||||
defer func(topic *pubsub.Subscription, ctx context.Context) {
|
||||
err := topic.Shutdown(ctx)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("fail to shutdown pubsub topic")
|
||||
}
|
||||
}(subscription, ctx)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
msg, err := subscription.Receive(ctx)
|
||||
log.Debug().Msg("received thumbnail generation request from pubsub topic")
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to receive message from pubsub topic")
|
||||
continue
|
||||
}
|
||||
if msg == nil {
|
||||
log.Warn().Msg("received nil message from pubsub topic")
|
||||
continue
|
||||
}
|
||||
groupId, err := uuid.Parse(msg.Metadata["group_id"])
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("group_id", msg.Metadata["group_id"]).Msg("failed to parse group ID from message metadata")
|
||||
}
|
||||
attachmentId, err := uuid.Parse(msg.Metadata["attachment_id"])
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("attachment_id", msg.Metadata["attachment_id"]).Msg("failed to parse attachment ID from message metadata")
|
||||
}
|
||||
err = app.repos.Attachments.CreateThumbnail(ctx, groupId, attachmentId, msg.Metadata["title"], msg.Metadata["path"])
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to create thumbnail")
|
||||
}
|
||||
msg.Ack()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if cfg.Options.GithubReleaseCheck {
|
||||
runner.AddPlugin(NewTask("get-latest-github-release", time.Hour, func(ctx context.Context) {
|
||||
log.Debug().Msg("running get latest github release")
|
||||
err := app.services.BackgroundService.GetLatestGithubRelease(context.Background())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to get latest github release")
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
if cfg.Debug.Enabled {
|
||||
runner.AddFunc("debug", func(ctx context.Context) error {
|
||||
debugserver := http.Server{
|
||||
Addr: fmt.Sprintf("%s:%s", cfg.Web.Host, cfg.Debug.Port),
|
||||
Handler: app.debugRouter(),
|
||||
ReadTimeout: cfg.Web.ReadTimeout,
|
||||
WriteTimeout: cfg.Web.WriteTimeout,
|
||||
IdleTimeout: cfg.Web.IdleTimeout,
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
_ = debugserver.Shutdown(context.Background())
|
||||
}()
|
||||
|
||||
log.Info().Msgf("Debug server is running on %s:%s", cfg.Web.Host, cfg.Debug.Port)
|
||||
return debugserver.ListenAndServe()
|
||||
})
|
||||
// Print the configuration to the console
|
||||
cfg.Print()
|
||||
}
|
||||
}
|
||||
@@ -129,7 +129,6 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
|
||||
r.Put("/items/{id}", chain.ToHandlerFunc(v1Ctrl.HandleItemUpdate(), userMW...))
|
||||
r.Patch("/items/{id}", chain.ToHandlerFunc(v1Ctrl.HandleItemPatch(), userMW...))
|
||||
r.Delete("/items/{id}", chain.ToHandlerFunc(v1Ctrl.HandleItemDelete(), userMW...))
|
||||
r.Post("/items/{id}/duplicate", chain.ToHandlerFunc(v1Ctrl.HandleItemDuplicate(), userMW...))
|
||||
|
||||
r.Post("/items/{id}/attachments", chain.ToHandlerFunc(v1Ctrl.HandleItemAttachmentCreate(), userMW...))
|
||||
r.Put("/items/{id}/attachments/{attachment_id}", chain.ToHandlerFunc(v1Ctrl.HandleItemAttachmentUpdate(), userMW...))
|
||||
@@ -158,8 +157,6 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
|
||||
a.mwRoles(RoleModeOr, authroles.RoleUser.String(), authroles.RoleAttachments.String()),
|
||||
}
|
||||
|
||||
r.Get("/products/search-from-barcode", chain.ToHandlerFunc(v1Ctrl.HandleProductSearchFromBarcode(a.conf.Barcode), userMW...))
|
||||
|
||||
r.Get("/qrcode", chain.ToHandlerFunc(v1Ctrl.HandleGenerateQRCode(), assetMW...))
|
||||
r.Get(
|
||||
"/items/{id}/attachments/{attachment_id}",
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/currencies"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/config"
|
||||
)
|
||||
|
||||
// setupStorageDir handles the creation and validation of the storage directory.
|
||||
func setupStorageDir(cfg *config.Config) error {
|
||||
if strings.HasPrefix(cfg.Storage.ConnString, "file:///./") {
|
||||
raw := strings.TrimPrefix(cfg.Storage.ConnString, "file:///./")
|
||||
clean := filepath.Clean(raw)
|
||||
absBase, err := filepath.Abs(clean)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to get absolute path for storage connection string")
|
||||
return fmt.Errorf("failed to get absolute path for storage connection string: %w", err)
|
||||
}
|
||||
absBase = strings.ReplaceAll(absBase, "\\", "/")
|
||||
storageDir := filepath.Join(absBase, cfg.Storage.PrefixPath)
|
||||
storageDir = strings.ReplaceAll(storageDir, "\\", "/")
|
||||
if !strings.HasPrefix(storageDir, absBase+"/") && storageDir != absBase {
|
||||
log.Error().Str("path", storageDir).Msg("invalid storage path: you tried to use a prefix that is not a subdirectory of the base path")
|
||||
return fmt.Errorf("invalid storage path: you tried to use a prefix that is not a subdirectory of the base path")
|
||||
}
|
||||
if err := os.MkdirAll(storageDir, 0o750); err != nil {
|
||||
log.Error().Err(err).Msg("failed to create data directory")
|
||||
return fmt.Errorf("failed to create data directory: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupDatabaseURL returns the database URL and ensures any required directories exist.
|
||||
func setupDatabaseURL(cfg *config.Config) (string, error) {
|
||||
databaseURL := ""
|
||||
switch strings.ToLower(cfg.Database.Driver) {
|
||||
case "sqlite3":
|
||||
databaseURL = cfg.Database.SqlitePath
|
||||
dbFilePath := strings.Split(cfg.Database.SqlitePath, "?")[0]
|
||||
dbDir := filepath.Dir(dbFilePath)
|
||||
if err := os.MkdirAll(dbDir, 0o755); err != nil {
|
||||
log.Error().Err(err).Str("path", dbDir).Msg("failed to create SQLite database directory")
|
||||
return "", fmt.Errorf("failed to create SQLite database directory: %w", err)
|
||||
}
|
||||
case "postgres":
|
||||
databaseURL = fmt.Sprintf("host=%s port=%s dbname=%s sslmode=%s", cfg.Database.Host, cfg.Database.Port, cfg.Database.Database, cfg.Database.SslMode)
|
||||
if cfg.Database.Username != "" {
|
||||
databaseURL += fmt.Sprintf(" user=%s", cfg.Database.Username)
|
||||
}
|
||||
if cfg.Database.Password != "" {
|
||||
databaseURL += fmt.Sprintf(" password=%s", cfg.Database.Password)
|
||||
}
|
||||
if cfg.Database.SslRootCert != "" {
|
||||
if _, err := os.Stat(cfg.Database.SslRootCert); err != nil {
|
||||
log.Error().Err(err).Str("path", cfg.Database.SslRootCert).Msg("SSL root certificate file is not accessible")
|
||||
return "", fmt.Errorf("SSL root certificate file is not accessible: %w", err)
|
||||
}
|
||||
databaseURL += fmt.Sprintf(" sslrootcert=%s", cfg.Database.SslRootCert)
|
||||
}
|
||||
if cfg.Database.SslCert != "" {
|
||||
if _, err := os.Stat(cfg.Database.SslCert); err != nil {
|
||||
log.Error().Err(err).Str("path", cfg.Database.SslCert).Msg("SSL certificate file is not accessible")
|
||||
return "", fmt.Errorf("SSL certificate file is not accessible: %w", err)
|
||||
}
|
||||
databaseURL += fmt.Sprintf(" sslcert=%s", cfg.Database.SslCert)
|
||||
}
|
||||
if cfg.Database.SslKey != "" {
|
||||
if _, err := os.Stat(cfg.Database.SslKey); err != nil {
|
||||
log.Error().Err(err).Str("path", cfg.Database.SslKey).Msg("SSL key file is not accessible")
|
||||
return "", fmt.Errorf("SSL key file is not accessible: %w", err)
|
||||
}
|
||||
databaseURL += fmt.Sprintf(" sslkey=%s", cfg.Database.SslKey)
|
||||
}
|
||||
default:
|
||||
log.Error().Str("driver", cfg.Database.Driver).Msg("unsupported database driver")
|
||||
return "", fmt.Errorf("unsupported database driver: %s", cfg.Database.Driver)
|
||||
}
|
||||
return databaseURL, nil
|
||||
}
|
||||
|
||||
// loadCurrencies loads currency data from config if provided.
|
||||
func loadCurrencies(cfg *config.Config) ([]currencies.CollectorFunc, error) {
|
||||
collectFuncs := []currencies.CollectorFunc{
|
||||
currencies.CollectDefaults(),
|
||||
}
|
||||
if cfg.Options.CurrencyConfig != "" {
|
||||
log.Info().Str("path", cfg.Options.CurrencyConfig).Msg("loading currency config file")
|
||||
content, err := os.ReadFile(cfg.Options.CurrencyConfig)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("path", cfg.Options.CurrencyConfig).Msg("failed to read currency config file")
|
||||
return nil, err
|
||||
}
|
||||
collectFuncs = append(collectFuncs, currencies.CollectJSON(bytes.NewReader(content)))
|
||||
}
|
||||
return collectFuncs, nil
|
||||
}
|
||||
@@ -943,48 +943,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/items/{id}/duplicate": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Items"
|
||||
],
|
||||
"summary": "Duplicate Item",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Item ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Duplicate Options",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/repo.DuplicateOptions"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/repo.ItemOut"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/items/{id}/maintenance": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -1853,41 +1811,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/products/search-from-barcode": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Items"
|
||||
],
|
||||
"summary": "Search EAN from Barcode",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "barcode to be searched",
|
||||
"name": "data",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.BarcodeProduct"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/qrcode": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -3140,54 +3063,6 @@ const docTemplate = `{
|
||||
"TypeTime"
|
||||
]
|
||||
},
|
||||
"repo.BarcodeProduct": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"barcode": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageBase64": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"item": {
|
||||
"$ref": "#/definitions/repo.ItemCreate"
|
||||
},
|
||||
"manufacturer": {
|
||||
"type": "string"
|
||||
},
|
||||
"modelNumber": {
|
||||
"description": "Identifications",
|
||||
"type": "string"
|
||||
},
|
||||
"notes": {
|
||||
"description": "Extras",
|
||||
"type": "string"
|
||||
},
|
||||
"search_engine_name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.DuplicateOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"copyAttachments": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyCustomFields": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyMaintenance": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyPrefix": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.Group": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -3698,7 +3573,7 @@ const docTemplate = `{
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"maxLength": 1000
|
||||
"maxLength": 255
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
|
||||
@@ -941,48 +941,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/items/{id}/duplicate": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Items"
|
||||
],
|
||||
"summary": "Duplicate Item",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Item ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Duplicate Options",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/repo.DuplicateOptions"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/repo.ItemOut"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/items/{id}/maintenance": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -1851,41 +1809,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/products/search-from-barcode": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Items"
|
||||
],
|
||||
"summary": "Search EAN from Barcode",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "barcode to be searched",
|
||||
"name": "data",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.BarcodeProduct"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/qrcode": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -3138,54 +3061,6 @@
|
||||
"TypeTime"
|
||||
]
|
||||
},
|
||||
"repo.BarcodeProduct": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"barcode": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageBase64": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"item": {
|
||||
"$ref": "#/definitions/repo.ItemCreate"
|
||||
},
|
||||
"manufacturer": {
|
||||
"type": "string"
|
||||
},
|
||||
"modelNumber": {
|
||||
"description": "Identifications",
|
||||
"type": "string"
|
||||
},
|
||||
"notes": {
|
||||
"description": "Extras",
|
||||
"type": "string"
|
||||
},
|
||||
"search_engine_name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.DuplicateOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"copyAttachments": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyCustomFields": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyMaintenance": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyPrefix": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.Group": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -3696,7 +3571,7 @@
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"maxLength": 1000
|
||||
"maxLength": 255
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
|
||||
@@ -646,38 +646,6 @@ definitions:
|
||||
- TypeNumber
|
||||
- TypeBoolean
|
||||
- TypeTime
|
||||
repo.BarcodeProduct:
|
||||
properties:
|
||||
barcode:
|
||||
type: string
|
||||
imageBase64:
|
||||
type: string
|
||||
imageURL:
|
||||
type: string
|
||||
item:
|
||||
$ref: '#/definitions/repo.ItemCreate'
|
||||
manufacturer:
|
||||
type: string
|
||||
modelNumber:
|
||||
description: Identifications
|
||||
type: string
|
||||
notes:
|
||||
description: Extras
|
||||
type: string
|
||||
search_engine_name:
|
||||
type: string
|
||||
type: object
|
||||
repo.DuplicateOptions:
|
||||
properties:
|
||||
copyAttachments:
|
||||
type: boolean
|
||||
copyCustomFields:
|
||||
type: boolean
|
||||
copyMaintenance:
|
||||
type: boolean
|
||||
copyPrefix:
|
||||
type: string
|
||||
type: object
|
||||
repo.Group:
|
||||
properties:
|
||||
createdAt:
|
||||
@@ -1023,7 +991,7 @@ definitions:
|
||||
color:
|
||||
type: string
|
||||
description:
|
||||
maxLength: 1000
|
||||
maxLength: 255
|
||||
type: string
|
||||
name:
|
||||
maxLength: 255
|
||||
@@ -1979,32 +1947,6 @@ paths:
|
||||
summary: Update Item Attachment
|
||||
tags:
|
||||
- Items Attachments
|
||||
/v1/items/{id}/duplicate:
|
||||
post:
|
||||
parameters:
|
||||
- description: Item ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
- description: Duplicate Options
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/repo.DuplicateOptions'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: Created
|
||||
schema:
|
||||
$ref: '#/definitions/repo.ItemOut'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Duplicate Item
|
||||
tags:
|
||||
- Items
|
||||
/v1/items/{id}/maintenance:
|
||||
get:
|
||||
parameters:
|
||||
@@ -2601,27 +2543,6 @@ paths:
|
||||
summary: Test Notifier
|
||||
tags:
|
||||
- Notifiers
|
||||
/v1/products/search-from-barcode:
|
||||
get:
|
||||
parameters:
|
||||
- description: barcode to be searched
|
||||
in: query
|
||||
name: data
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/repo.BarcodeProduct'
|
||||
type: array
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Search EAN from Barcode
|
||||
tags:
|
||||
- Items
|
||||
/v1/qrcode:
|
||||
get:
|
||||
parameters:
|
||||
|
||||
216
backend/go.mod
216
backend/go.mod
@@ -1,11 +1,11 @@
|
||||
module github.com/sysadminsmedia/homebox/backend
|
||||
|
||||
go 1.24.0
|
||||
go 1.24
|
||||
|
||||
toolchain go1.24.3
|
||||
|
||||
require (
|
||||
entgo.io/ent v0.14.5
|
||||
entgo.io/ent v0.14.4
|
||||
github.com/ardanlabs/conf/v3 v3.8.0
|
||||
github.com/containrrr/shoutrrr v0.8.0
|
||||
github.com/evanoberholster/imagemeta v0.3.1
|
||||
@@ -14,91 +14,90 @@ require (
|
||||
github.com/gen2brain/jpegxl v0.4.5
|
||||
github.com/gen2brain/webp v0.5.5
|
||||
github.com/go-chi/chi/v5 v5.2.2
|
||||
github.com/go-playground/validator/v10 v10.27.0
|
||||
github.com/go-playground/validator/v10 v10.26.0
|
||||
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/schema v1.4.1
|
||||
github.com/hay-kot/httpkit v0.0.11
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/mattn/go-sqlite3 v1.14.32
|
||||
github.com/olahol/melody v1.3.0
|
||||
github.com/mattn/go-sqlite3 v1.14.28
|
||||
github.com/olahol/melody v1.2.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pressly/goose/v3 v3.24.3
|
||||
github.com/rs/zerolog v1.34.0
|
||||
github.com/shirou/gopsutil/v4 v4.25.7
|
||||
github.com/shirou/gopsutil/v4 v4.25.5
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/swaggo/http-swagger/v2 v2.0.2
|
||||
github.com/swaggo/swag v1.16.6
|
||||
github.com/swaggo/swag v1.16.4
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.5
|
||||
github.com/yeqown/go-qrcode/writer/standard v1.3.0
|
||||
github.com/zeebo/blake3 v0.2.4
|
||||
go.balki.me/anyhttp v0.5.2
|
||||
gocloud.dev v0.43.0
|
||||
gocloud.dev/pubsub/kafkapubsub v0.43.0
|
||||
gocloud.dev/pubsub/natspubsub v0.43.0
|
||||
gocloud.dev/pubsub/rabbitpubsub v0.43.0
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/image v0.30.0
|
||||
golang.org/x/text v0.28.0
|
||||
modernc.org/sqlite v1.38.2
|
||||
gocloud.dev v0.41.0
|
||||
gocloud.dev/pubsub/kafkapubsub v0.41.0
|
||||
gocloud.dev/pubsub/natspubsub v0.41.0
|
||||
gocloud.dev/pubsub/rabbitpubsub v0.41.0
|
||||
golang.org/x/crypto v0.39.0
|
||||
golang.org/x/image v0.28.0
|
||||
modernc.org/sqlite v1.37.1
|
||||
)
|
||||
|
||||
require (
|
||||
ariga.io/atlas v0.32.1-0.20250325101103-175b25e1c1b9 // indirect
|
||||
cel.dev/expr v0.24.0 // indirect
|
||||
cloud.google.com/go v0.121.4 // indirect
|
||||
cloud.google.com/go/auth v0.16.4 // indirect
|
||||
ariga.io/atlas v0.31.1-0.20250212144724-069be8033e83 // indirect
|
||||
cel.dev/expr v0.22.1 // indirect
|
||||
cloud.google.com/go v0.120.0 // indirect
|
||||
cloud.google.com/go/auth v0.15.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.8.0 // indirect
|
||||
cloud.google.com/go/iam v1.5.2 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.2 // indirect
|
||||
cloud.google.com/go/pubsub v1.49.0 // indirect
|
||||
cloud.google.com/go/storage v1.55.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/iam v1.4.2 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.1 // indirect
|
||||
cloud.google.com/go/pubsub v1.48.0 // indirect
|
||||
cloud.google.com/go/storage v1.51.0 // indirect
|
||||
github.com/Azure/azure-amqp-common-go/v3 v3.2.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.9.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.8.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0 // indirect
|
||||
github.com/Azure/go-amqp v1.4.0 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.1 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect
|
||||
github.com/IBM/sarama v1.45.2 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
|
||||
github.com/IBM/sarama v1.45.1 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/agext/levenshtein v1.2.1 // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.55.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.70 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.84 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36 // indirect
|
||||
github.com/aws/aws-sdk-go v1.55.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.65 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.69 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.36 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.84.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.34.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.38.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 // indirect
|
||||
github.com/aws/smithy-go v1.22.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.34.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.38.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 // indirect
|
||||
github.com/aws/smithy-go v1.22.3 // indirect
|
||||
github.com/bmatcuk/doublestar v1.3.4 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/eapache/go-resiliency v1.7.0 // indirect
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect
|
||||
@@ -106,33 +105,33 @@ require (
|
||||
github.com/ebitengine/purego v0.8.4 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fogleman/gg v1.3.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.1 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-openapi/inflect v0.19.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.2 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/spec v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/spec v0.20.6 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.3 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/wire v0.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.18.1 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.13.0 // indirect
|
||||
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
|
||||
github.com/jcmturner/gofork v1.7.6 // indirect
|
||||
@@ -141,67 +140,68 @@ require (
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mfridman/interpolate v0.0.2 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/nats-io/nats.go v1.44.0 // indirect
|
||||
github.com/nats-io/nkeys v0.4.11 // indirect
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
|
||||
github.com/nats-io/nats.go v1.40.1 // indirect
|
||||
github.com/nats-io/nkeys v0.4.10 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/philhofer/fwd v1.1.2 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/sethvargo/go-retry v0.3.0 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.2 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.0 // indirect
|
||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||
github.com/tinylib/msgp v1.3.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
github.com/tinylib/msgp v1.1.8 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/yeqown/reedsolomon v1.0.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
github.com/zclconf/go-cty v1.14.4 // indirect
|
||||
github.com/zclconf/go-cty-yaml v1.1.0 // indirect
|
||||
github.com/zeebo/errs v1.4.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.37.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
|
||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/oauth2 v0.28.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
||||
google.golang.org/api v0.247.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250715232539-7130f93afb79 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a // indirect
|
||||
google.golang.org/grpc v1.74.2 // indirect
|
||||
google.golang.org/protobuf v1.36.7 // indirect
|
||||
google.golang.org/api v0.228.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250324211829-b45e905df463 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
|
||||
google.golang.org/grpc v1.71.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.66.7 // indirect
|
||||
modernc.org/libc v1.65.7 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
||||
552
backend/go.sum
552
backend/go.sum
@@ -1,47 +1,48 @@
|
||||
ariga.io/atlas v0.32.1-0.20250325101103-175b25e1c1b9 h1:E0wvcUXTkgyN4wy4LGtNzMNGMytJN8afmIWXJVMi4cc=
|
||||
ariga.io/atlas v0.32.1-0.20250325101103-175b25e1c1b9/go.mod h1:Oe1xWPuu5q9LzyrWfbZmEZxFYeu4BHTyzfjeW2aZp/w=
|
||||
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
|
||||
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||
cloud.google.com/go v0.121.4 h1:cVvUiY0sX0xwyxPwdSU2KsF9knOVmtRyAMt8xou0iTs=
|
||||
cloud.google.com/go v0.121.4/go.mod h1:XEBchUiHFJbz4lKBZwYBDHV/rSyfFktk737TLDU089s=
|
||||
cloud.google.com/go/auth v0.16.4 h1:fXOAIQmkApVvcIn7Pc2+5J8QTMVbUGLscnSVNl11su8=
|
||||
cloud.google.com/go/auth v0.16.4/go.mod h1:j10ncYwjX/g3cdX7GpEzsdM+d+ZNsXAbb6qXA7p1Y5M=
|
||||
ariga.io/atlas v0.31.1-0.20250212144724-069be8033e83 h1:nX4HXncwIdvQ8/8sIUIf1nyCkK8qdBaHQ7EtzPpuiGE=
|
||||
ariga.io/atlas v0.31.1-0.20250212144724-069be8033e83/go.mod h1:Oe1xWPuu5q9LzyrWfbZmEZxFYeu4BHTyzfjeW2aZp/w=
|
||||
cel.dev/expr v0.22.1 h1:xoFEsNh972Yzey8N9TCPx2nDvMN7TMhQEzxLuj/iRrI=
|
||||
cel.dev/expr v0.22.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=
|
||||
cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=
|
||||
cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
|
||||
cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA=
|
||||
cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw=
|
||||
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
|
||||
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
|
||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/iam v1.4.2 h1:4AckGYAYsowXeHzsn/LCKWIwSWLkdb0eGjH8wWkd27Q=
|
||||
cloud.google.com/go/iam v1.4.2/go.mod h1:REGlrt8vSlh4dfCJfSEcNjLGq75wW75c5aU3FLOYq34=
|
||||
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
|
||||
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
|
||||
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
|
||||
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
|
||||
cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM=
|
||||
cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U=
|
||||
cloud.google.com/go/pubsub v1.49.0 h1:5054IkbslnrMCgA2MAEPcsN3Ky+AyMpEZcii/DoySPo=
|
||||
cloud.google.com/go/pubsub v1.49.0/go.mod h1:K1FswTWP+C1tI/nfi3HQecoVeFvL4HUOB1tdaNXKhUY=
|
||||
cloud.google.com/go/storage v1.55.0 h1:NESjdAToN9u1tmhVqhXCaCwYBuvEhZLLv0gBr+2znf0=
|
||||
cloud.google.com/go/storage v1.55.0/go.mod h1:ztSmTTwzsdXe5syLVS0YsbFxXuvEmEyZj7v7zChEmuY=
|
||||
cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4=
|
||||
cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
|
||||
entgo.io/ent v0.14.5 h1:Rj2WOYJtCkWyFo6a+5wB3EfBRP0rnx1fMk6gGA0UUe4=
|
||||
entgo.io/ent v0.14.5/go.mod h1:zTzLmWtPvGpmSwtkaayM2cm5m819NdM7z7tYPq3vN0U=
|
||||
cloud.google.com/go/longrunning v0.6.6 h1:XJNDo5MUfMM05xK3ewpbSdmt7R2Zw+aQEMbdQR65Rbw=
|
||||
cloud.google.com/go/longrunning v0.6.6/go.mod h1:hyeGJUrPHcx0u2Uu1UFSoYZLn4lkMrccJig0t4FI7yw=
|
||||
cloud.google.com/go/monitoring v1.24.1 h1:vKiypZVFD/5a3BbQMvI4gZdl8445ITzXFh257XBgrS0=
|
||||
cloud.google.com/go/monitoring v1.24.1/go.mod h1:Z05d1/vn9NaujqY2voG6pVQXoJGbp+r3laV+LySt9K0=
|
||||
cloud.google.com/go/pubsub v1.48.0 h1:ntFpQVrr10Wj/GXSOpxGmexGynldv/bFp25H0jy8aOs=
|
||||
cloud.google.com/go/pubsub v1.48.0/go.mod h1:AAtyjyIT/+zaY1ERKFJbefOvkUxRDNp3nD6TdfdqUZk=
|
||||
cloud.google.com/go/storage v1.51.0 h1:ZVZ11zCiD7b3k+cH5lQs/qcNaoSz3U9I0jgwVzqDlCw=
|
||||
cloud.google.com/go/storage v1.51.0/go.mod h1:YEJfu/Ki3i5oHC/7jyTgsGZwdQ8P9hqMqvpi5kRKGgc=
|
||||
cloud.google.com/go/trace v1.11.5 h1:CALS1loyxJMnRiCwZSpdf8ac7iCsjreMxFD2WGxzzHU=
|
||||
cloud.google.com/go/trace v1.11.5/go.mod h1:TwblCcqNInriu5/qzaeYEIH7wzUcchSdeY2l5wL3Eec=
|
||||
entgo.io/ent v0.14.4 h1:/DhDraSLXIkBhyiVoJeSshr4ZYi7femzhj6/TckzZuI=
|
||||
entgo.io/ent v0.14.4/go.mod h1:aDPE/OziPEu8+OWbzy4UlvWmD2/kbRuWfK2A40hcxJM=
|
||||
github.com/Azure/azure-amqp-common-go/v3 v3.2.3 h1:uDF62mbd9bypXWi19V1bN5NZEO84JqgmI5G73ibAmrk=
|
||||
github.com/Azure/azure-amqp-common-go/v3 v3.2.3/go.mod h1:7rPmbSfszeovxGfc5fSAXE4ehlXQZHpMja2OtxC2Tas=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 h1:Wc1ml6QlJs2BHQ/9Bqu1jiyggbsSjramq2oUmp5WeIo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 h1:DSDNVxqkoXJiko6x8a90zidoYqnYYa6c1MTzDKzKkTo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1/go.mod h1:zGqV2R4Cr/k8Uye5w+dgQ06WJtEcbQG/8J7BB6hnCr4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.9.1 h1:CRZwf68N55u7ZZo3Xx2ynuqEA6k5GZfwsEUkU8qsAPk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.9.1/go.mod h1:NydgUaroiShkgOcb+X6OUdS3RalWBrvDNtOyFHJtsZY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.0 h1:LR0kAX9ykz8G4YgLCaRDVJ3+n43R8MneB5dTy2konZo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.0/go.mod h1:DWAciXemNf++PQJLeXUB4HHH5OpsAh12HZnu2wXE1jA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1 h1:lhZdRq7TIx0GJQvSyX2Si406vrYsov2FXGp/RnSEtcs=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1/go.mod h1:8cl44BDmi+effbARHMQjgOKA2AYvcohNm7KEt42mSV8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.8.0 h1:JNgM3Tz592fUHU2vgwgvOgKxo5s9Ki0y2wicBeckn70=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.8.0/go.mod h1:6vUKmzY17h6dpn9ZLAhM4R/rcrltBeq52qZIkUR7Oro=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 h1:PiSrjRPpkQNjrM8H0WwKMnZUdu1RGMtd/LdGKUrOo+c=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0 h1:UXT0o77lXQrikd1kgwIPQOUect7EoR/+sbP4wQKdzxM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0/go.mod h1:cTvi54pg19DoT07ekoeMgE/taAwNtCShVeZqA+Iv2xI=
|
||||
github.com/Azure/go-amqp v0.17.0/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg=
|
||||
github.com/Azure/go-amqp v1.4.0 h1:Xj3caqi4comOF/L1Uc5iuBxR/pB6KumejC01YQOqOR4=
|
||||
github.com/Azure/go-amqp v1.4.0/go.mod h1:vZAogwdrkbyK3Mla8m/CxSc/aKdnTZ4IbPxl51Y5WZE=
|
||||
@@ -59,85 +60,91 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJ
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 h1:UQUsRi8WTzhZntp5313l+CHIAT95ojUI2lpP/ExlZa4=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0/go.mod h1:jUZ5LYlw40WMd07qxcQJD5M40aUxrfwqQX1g7zxYnrQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo=
|
||||
github.com/IBM/sarama v1.45.2 h1:8m8LcMCu3REcwpa7fCP6v2fuPuzVwXDAM2DOv3CBrKw=
|
||||
github.com/IBM/sarama v1.45.2/go.mod h1:ppaoTcVdGv186/z6MEKsMm70A5fwJfRTpstI37kVn3Y=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
|
||||
github.com/IBM/sarama v1.45.1 h1:nY30XqYpqyXOXSNoe2XCgjj9jklGM1Ye94ierUb1jQ0=
|
||||
github.com/IBM/sarama v1.45.1/go.mod h1:qifDhA3VWSrQ1TjSMyxDl3nYL3oX2C83u+G6L79sq4w=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
|
||||
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/ardanlabs/conf/v3 v3.8.0 h1:Mvv2wZJz8tIl705m5BU3ZRCP1V6TKY6qebA8i4sykrY=
|
||||
github.com/ardanlabs/conf/v3 v3.8.0/go.mod h1:XlL9P0quWP4m1weOVFmlezabinbZLI05niDof/+Ochk=
|
||||
github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
|
||||
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.5 h1:0OF9RiEMEdDdZEMqF9MRjevyxAQcf6gY+E7vwBILFj0=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.5/go.mod h1:EYrzvCCN9CMUTa5+6lf6MM4tq3Zjp8UhSGR/cBsjai0=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 h1:12SpdwU8Djs+YGklkinSSlcrPyj3H4VifVsKf78KbwA=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11/go.mod h1:dd+Lkp6YmMryke+qxW/VnKyhMBDTYP41Q2Bb+6gNZgY=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.17 h1:jSuiQ5jEe4SAMH6lLRMY9OVC+TqJLP5655pBGjmnjr0=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.17/go.mod h1:9P4wwACpbeXs9Pm9w1QTh6BwWwJjwYvJ1iCt5QbCXh8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.70 h1:ONnH5CM16RTXRkS8Z1qg7/s2eDOhHhaXVd72mmyv4/0=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.70/go.mod h1:M+lWhhmomVGgtuPOhO85u4pEa3SmssPTdcYpP/5J/xc=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 h1:KAXP9JSHO1vKGCr5f4O6WmlVKLFFXgWYAGoJosorxzU=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32/go.mod h1:h4Sg6FQdexC1yYG9RDnOvLbW1a/P986++/Y/a+GyEM8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.84 h1:cTXRdLkpBanlDwISl+5chq5ui1d1YWg4PWMR9c3kXyw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.84/go.mod h1:kwSy5X7tfIHN39uucmjQVs2LvDdXEjQucgQQEqCggEo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36 h1:SsytQyTMHMDPspp+spo7XwXTP44aJZZAC7fBV2C5+5s=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36/go.mod h1:Q1lnJArKRXkenyog6+Y+zr7WDpk4e6XlR6gs20bbeNo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36 h1:i2vNHQiXUvKhs3quBR6aqlgJaiaexz/aNvdCktW/kAM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36/go.mod h1:UdyGa7Q91id/sdyHPwth+043HhmP6yP9MBHgbZM0xo8=
|
||||
github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
|
||||
github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.12 h1:Y/2a+jLPrPbHpFkpAAYkVEtJmxORlXoo5k2g1fa2sUo=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.12/go.mod h1:xse1YTjmORlb/6fhkWi8qJh3cvZi4JoVNhc+NbJt4kI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.65 h1:q+nV2yYegofO/SUXruT+pn4KxkxmaQ++1B/QedcKBFM=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.65/go.mod h1:4zyjAuGOdikpNYiSGpsGz8hLGmUzlY8pc8r9QQ/RXYQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.69 h1:6VFPH/Zi9xYFMJKPQOX5URYkQoXRWeJ7V/7Y6ZDYoms=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.69/go.mod h1:GJj8mmO6YT6EqgduWocwhMoxTLFitkhIrK+owzrYL2I=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.36 h1:GMYy2EOWfzdP3wfVAGXBNKY5vK4K8vMET4sYOYltmqs=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.36/go.mod h1:gDhdAV6wL3PmPqBhiPbnlS447GoWs8HTTOYef9/9Inw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 h1:CXV68E2dNqhuynZJPB80bhPQwAKqBWVer887figW6Jc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4/go.mod h1:/xFi9KtvBXP97ppCz1TAEvU1Uf66qvid89rbem3wCzQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.4 h1:nAP2GYbfh8dd2zGZqFRSMlq+/F6cMPBUuCsGAMkN074=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.4/go.mod h1:LT10DsiGjLWh4GbjInf9LQejkYEhBgBCjLG5+lvk4EE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 h1:t0E6FzREdtCsiLIoLCWsYliNsRBgyGD/MCK571qk4MI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17/go.mod h1:ygpklyoaypuyDvOM5ujWGrYWpAK3h7ugnmKCU/76Ys4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.17 h1:qcLWgdhq45sDM9na4cvXax9dyLitn8EYBRl8Ak4XtG4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.17/go.mod h1:M+jkjBFZ2J6DJrjMv2+vkBbuht6kxJYtJiwoVgX4p4U=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.84.0 h1:0reDqfEN+tB+sozj2r92Bep8MEwBZgtAXTND1Kk9OXg=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.84.0/go.mod h1:kUklwasNoCn5YpyAqC/97r6dzTA1SRKJfKq16SXeoDU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.34.7 h1:OBuZE9Wt8h2imuRktu+WfjiTGrnYdCIJg8IX92aalHE=
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.34.7/go.mod h1:4WYoZAhHt+dWYpoOQUgkUKfuQbE6Gg/hW4oXE0pKS9U=
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.38.8 h1:80dpSqWMwx2dAm30Ib7J6ucz1ZHfiv5OCRwN/EnCOXQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.38.8/go.mod h1:IzNt/udsXlETCdvBOL0nmyMe2t9cGmXmZgsdoZGYYhI=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 h1:AIRJ3lfb2w/1/8wOOSqYb9fUKGwQbtysJ2H1MofRUPg=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.5/go.mod h1:b7SiVprpU+iGazDUqvRSLf5XmCdn+JtT1on7uNL6Ipc=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 h1:BpOxT3yhLwSJ77qIY3DoHAQjZsc4HEGfMCE4NGy3uFg=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3/go.mod h1:vq/GQR1gOFLquZMSrxUK/cpvKCNVYibNyJ1m7JrU88E=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 h1:NFOJ/NXEGV4Rq//71Hs1jC/NvPs1ezajK+yQmkwnPV0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0/go.mod h1:7ph2tGpfQvwzgistp2+zga9f+bCjlQJPkPUmMgDSD7w=
|
||||
github.com/aws/smithy-go v1.22.4 h1:uqXzVZNuNexwc/xrh6Tb56u89WDlJY6HS+KC0S4QSjw=
|
||||
github.com/aws/smithy-go v1.22.4/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 h1:lguz0bmOoGzozP9XfRJR1QIayEYo+2vP/No3OfLF0pU=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.2 h1:jIiopHEV22b4yQP2q36Y0OmwLbsxNWdWwfZRR5QRRO4=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.2/go.mod h1:U5SNqwhXB3Xe6F47kXvWihPl/ilGaEDe8HD/50Z9wxc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.34.2 h1:PajtbJ/5bEo6iUAIGMYnK8ljqg2F1h4mMCGh1acjN30=
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.34.2/go.mod h1:PJtxxMdj747j8DeZENRTTYAz/lx/pADn/U0k7YNNiUY=
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.38.3 h1:j5BchjfDoS7K26vPdyJlyxBIIBGDflq3qjjJKBDlbcI=
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.38.3/go.mod h1:Bar4MrRxeqdn6XIh8JGfiXuFRmyrrsZNTJotxEJmWW0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.2 h1:pdgODsAhGo4dvzC3JAG5Ce0PX8kWXrTZGx+jxADD+5E=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.2/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.0 h1:90uX0veLKcdHVfvxhkWUQSCi5VabtwMLFutYiRke4oo=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.0/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
|
||||
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
|
||||
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
|
||||
github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=
|
||||
github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k=
|
||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE=
|
||||
github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
||||
github.com/containrrr/shoutrrr v0.8.0 h1:mfG2ATzIS7NR2Ec6XL+xyoHzN97H8WPjir8aYzJUSec=
|
||||
github.com/containrrr/shoutrrr v0.8.0/go.mod h1:ioyQAyu1LJY6sILuNyKaQaw+9Ttik5QePU8atnAdO2o=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
@@ -151,18 +158,22 @@ github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
|
||||
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=
|
||||
github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
|
||||
github.com/evanoberholster/imagemeta v0.3.1 h1:E4GUjXcvlVMjP9joN25+bBNf3Al3MTTfMqCrDOCW+LE=
|
||||
github.com/evanoberholster/imagemeta v0.3.1/go.mod h1:V0vtDJmjTqvwAYO8r+u33NRVIMXQb0qSqEfImoKEiXM=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||
@@ -170,8 +181,8 @@ github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzP
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/gen2brain/avif v0.4.4 h1:Ga/ss7qcWWQm2bxFpnjYjhJsNfZrWs5RsyklgFjKRSE=
|
||||
github.com/gen2brain/avif v0.4.4/go.mod h1:/XCaJcjZraQwKVhpu9aEd9aLOssYOawLvhMBtmHVGqk=
|
||||
github.com/gen2brain/heic v0.4.5 h1:Cq3hPu6wwlTJNv2t48ro3oWje54h82Q5pALeCBNgaSk=
|
||||
@@ -182,33 +193,33 @@ github.com/gen2brain/webp v0.5.5 h1:MvQR75yIPU/9nSqYT5h13k4URaJK3gf9tgz/ksRbyEg=
|
||||
github.com/gen2brain/webp v0.5.5/go.mod h1:xOSMzp4aROt2KFW++9qcK/RBTOVC2S9tJG66ip/9Oc0=
|
||||
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
|
||||
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||
github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI=
|
||||
github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
|
||||
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
|
||||
github.com/go-openapi/jsonpointer v0.21.2 h1:AqQaNADVwq/VnkCmQg6ogE+M3FOsKTytwges0JdwVuA=
|
||||
github.com/go-openapi/jsonpointer v0.21.2/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
|
||||
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
|
||||
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
|
||||
github.com/go-openapi/spec v0.20.6 h1:ich1RQ3WDbfoeTqTAb+5EIxNmpKVJZWBNah9RAT0jIQ=
|
||||
github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
|
||||
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||
@@ -216,18 +227,37 @@ github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3a
|
||||
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1 h1:FWNFq4fM1wPfcK40yHE5UO3RUdSNPaBC+j3PokzA6OQ=
|
||||
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
@@ -242,20 +272,21 @@ github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
|
||||
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
||||
github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E=
|
||||
github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -264,8 +295,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/hcl/v2 v2.18.1 h1:6nxnOJFku1EuSawSD81fuviYUV8DxFr3fp2dUi3ZYSo=
|
||||
github.com/hashicorp/hcl/v2 v2.18.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE=
|
||||
github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgCNc=
|
||||
github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0=
|
||||
github.com/hay-kot/httpkit v0.0.11 h1:ZdB2uqsFBSDpfUoClGK5c5orjBjQkEVSXh7fZX5FKEk=
|
||||
github.com/hay-kot/httpkit v0.0.11/go.mod h1:0kZdk5/swzdfqfg2c6pBWimcgeJ9PTyO97EbHnYl2Sw=
|
||||
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
|
||||
@@ -290,14 +321,17 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
|
||||
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
|
||||
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
|
||||
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
@@ -308,44 +342,45 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
|
||||
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
|
||||
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
|
||||
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
|
||||
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
|
||||
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/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/nats-io/jwt/v2 v2.5.0 h1:WQQ40AAlqqfx+f6ku+i0pOVm+ASirD4fUh+oQsiE9Ak=
|
||||
github.com/nats-io/jwt/v2 v2.5.0/go.mod h1:24BeQtRwxRV8ruvC4CojXlx/WQ/VjuwlYiH+vu/+ibI=
|
||||
github.com/nats-io/nats-server/v2 v2.9.23 h1:6Wj6H6QpP9FMlpCyWUaNu2yeZ/qGj+mdRkZ1wbikExU=
|
||||
github.com/nats-io/nats-server/v2 v2.9.23/go.mod h1:wEjrEy9vnqIGE4Pqz4/c75v9Pmaq7My2IgFmnykc4C0=
|
||||
github.com/nats-io/nats.go v1.44.0 h1:ECKVrDLdh/kDPV1g0gAQ+2+m2KprqZK5O/eJAyAnH2M=
|
||||
github.com/nats-io/nats.go v1.44.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
||||
github.com/nats-io/nats.go v1.40.1 h1:MLjDkdsbGUeCMKFyCFoLnNn/HDTqcgVa3EQm+pMNDPk=
|
||||
github.com/nats-io/nats.go v1.40.1/go.mod h1:wV73x0FSI/orHPSYoyMeJB+KajMDoWyXmFaRrrYaaTo=
|
||||
github.com/nats-io/nkeys v0.4.10 h1:glmRrpCmYLHByYcePvnTBEAwawwapjCPMjy2huw20wc=
|
||||
github.com/nats-io/nkeys v0.4.10/go.mod h1:OjRrnIKnWBFl+s4YK5ChQfvHP2fxqZexrKJoVVyWB3U=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/olahol/melody v1.3.0 h1:n7UlKiQnxVrgxKoM0d7usZiN+Z0y2lVENtYLgKtXS6s=
|
||||
github.com/olahol/melody v1.3.0/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7CvJgJM4=
|
||||
github.com/olahol/melody v1.2.1 h1:xdwRkzHxf+B0w4TKbGpUSSkV516ZucQZJIWLztOWICQ=
|
||||
github.com/olahol/melody v1.2.1/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7CvJgJM4=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
||||
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
|
||||
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
|
||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
@@ -354,19 +389,19 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/pressly/goose/v3 v3.24.3 h1:DSWWNwwggVUsYZ0X2VitiAa9sKuqtBfe+Jr9zFGwWlM=
|
||||
github.com/pressly/goose/v3 v3.24.3/go.mod h1:v9zYL4xdViLHCUUJh/mhjnm6JrK7Eul8AS93IxiZM4E=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
|
||||
github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
|
||||
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
@@ -374,16 +409,14 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE=
|
||||
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
|
||||
github.com/shirou/gopsutil/v4 v4.25.7 h1:bNb2JuqKuAu3tRlPv5piSmBZyMfecwQ+t/ILq+1JqVM=
|
||||
github.com/shirou/gopsutil/v4 v4.25.7/go.mod h1:XV/egmwJtd3ZQjBpJVY5kndsiOO4IRqy9TQnmm6VP7U=
|
||||
github.com/shirou/gopsutil/v4 v4.25.5 h1:rtd9piuSMGeU8g1RMXjZs9y9luK5BwtnG7dZaQUJAsc=
|
||||
github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
|
||||
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/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
@@ -395,20 +428,20 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU=
|
||||
github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0=
|
||||
github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw=
|
||||
github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
|
||||
github.com/swaggo/http-swagger/v2 v2.0.2 h1:FKCdLsl+sFCx60KFsyM0rDarwiUSZ8DqbfSyIKC9OBg=
|
||||
github.com/swaggo/http-swagger/v2 v2.0.2/go.mod h1:r7/GBkAWIfK6E/OLnE8fXnviHiDeAHmgIyooa4xm3AQ=
|
||||
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
|
||||
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
|
||||
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
|
||||
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
|
||||
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
||||
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
|
||||
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
|
||||
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
|
||||
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.5 h1:HCOe2bSjkhZyYoyyNaXNzh4DJZll6inVJQQw+8228Zk=
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.5/go.mod h1:uHpt9CM0V1HeXLz+Wg5MN50/sI/fQhfkZlOM+cOTHxw=
|
||||
github.com/yeqown/go-qrcode/writer/standard v1.3.0 h1:chdyhEfRtUPgQtuPeaWVGQ/TQx4rE1PqeoW3U+53t34=
|
||||
@@ -426,102 +459,121 @@ github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
|
||||
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
||||
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
|
||||
github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
|
||||
github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
go.balki.me/anyhttp v0.5.2 h1:et4tCDXLeXpWfMNvRKG7ojfrnlr3du7cEaG966MLSpA=
|
||||
go.balki.me/anyhttp v0.5.2/go.mod h1:JhfekOIjgVODoVqUCficjpIgmB3wwlB7jhN0eN2EZ/s=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.37.0 h1:B+WbN9RPsvobe6q4vP6KgM8/9plR/HNjgGBrfcOlweA=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.37.0/go.mod h1:K5zQ3TT7p2ru9Qkzk0bKtCql0RGkPj9pRjpXgZJZ+rU=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 h1:rbRJ8BBoVMsQShESYZ0FkvcITu8X8QNwJogcLUmDNNw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0/go.mod h1:ru6KHrNtNHxM4nD/vd6QrLVWgKhxPYgblq4VAtNawTQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.37.0 h1:6VjV6Et+1Hd2iLZEPtdV7vie80Yyqf7oikJLjQ/myi0=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.37.0/go.mod h1:u8hcp8ji5gaM/RfcOo8z9NMnf1pVLfVY7lBY2VOGuUU=
|
||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
gocloud.dev v0.43.0 h1:aW3eq4RMyehbJ54PMsh4hsp7iX8cO/98ZRzJJOzN/5M=
|
||||
gocloud.dev v0.43.0/go.mod h1:eD8rkg7LhKUHrzkEdLTZ+Ty/vgPHPCd+yMQdfelQVu4=
|
||||
gocloud.dev/pubsub/kafkapubsub v0.43.0 h1:Kgwi0na69W3RgxEffEkdrMhox6A3Q0gajoJtjHGVr/s=
|
||||
gocloud.dev/pubsub/kafkapubsub v0.43.0/go.mod h1:uKI0CXuj7HJ/YnnOLQ3VkDnuUnkz+q/d+tRzmfhmOOU=
|
||||
gocloud.dev/pubsub/natspubsub v0.43.0 h1:k35tFoaorvD9Fa26zVEEzyXiMOEyXNHc0pBOmRYvQI0=
|
||||
gocloud.dev/pubsub/natspubsub v0.43.0/go.mod h1:xJn8TO8pGYieDn6AsRFsYfhQW8cnC+xGmG9APGNxkpQ=
|
||||
gocloud.dev/pubsub/rabbitpubsub v0.43.0 h1:6nNZFSlJ1dk2GujL8PFltfLz3vC6IbrpjGS4FTduo1s=
|
||||
gocloud.dev/pubsub/rabbitpubsub v0.43.0/go.mod h1:sEaueAGat+OASRoB3QDkghCtibKttgg7X6zsPTm1pl0=
|
||||
gocloud.dev v0.41.0 h1:qBKd9jZkBKEghYbP/uThpomhedK5s2Gy6Lz7h/zYYrM=
|
||||
gocloud.dev v0.41.0/go.mod h1:IetpBcWLUwroOOxKr90lhsZ8vWxeSkuszBnW62sbcf0=
|
||||
gocloud.dev/pubsub/kafkapubsub v0.41.0 h1:Ft6YB77ejqk++VjW51UP39RH/WDAMtv6ed3+PHMxBzg=
|
||||
gocloud.dev/pubsub/kafkapubsub v0.41.0/go.mod h1:kJf4c6b+4yJk6nXmv33yXKblbrgWmrYCzI5QEsr27G0=
|
||||
gocloud.dev/pubsub/natspubsub v0.41.0 h1:UxNb0DiAzdnyHut6jcCG7u6lsB/hzxTyZ/RHWeCUJ4Q=
|
||||
gocloud.dev/pubsub/natspubsub v0.41.0/go.mod h1:uCBKjwvIcuNuf3+ft4wUI9hPHHKQvroxq9ZPB/410ac=
|
||||
gocloud.dev/pubsub/rabbitpubsub v0.41.0 h1:RutvHbacZxlFr0t3wlr+kz63j53UOfHY3PJR8NKN1EI=
|
||||
gocloud.dev/pubsub/rabbitpubsub v0.41.0/go.mod h1:s7oQXOlQ2FOj8XmYMv5Ocgs1t+8hIXfsKaWGgECM9SQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 h1:SbTAbRFnd5kjQXbczszQ0hdk3ctwYf3qBNH9jIsGclE=
|
||||
golang.org/x/exp v0.0.0-20250813145105-42675adae3e6/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
|
||||
golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4=
|
||||
golang.org/x/image v0.30.0/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE=
|
||||
golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
@@ -529,39 +581,66 @@ golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
google.golang.org/api v0.247.0 h1:tSd/e0QrUlLsrwMKmkbQhYVa109qIintOls2Wh6bngc=
|
||||
google.golang.org/api v0.247.0/go.mod h1:r1qZOPmxXffXg6xS5uhx16Fa/UFY8QU/K4bfKrnvovM=
|
||||
google.golang.org/genproto v0.0.0-20250715232539-7130f93afb79 h1:Nt6z9UHqSlIdIGJdz6KhTIs2VRx/iOsA5iE8bmQNcxs=
|
||||
google.golang.org/genproto v0.0.0-20250715232539-7130f93afb79/go.mod h1:kTmlBHMPqR5uCZPBvwa2B18mvubkjyY3CRLI0c6fj0s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 h1:iOye66xuaAK0WnkPuhQPUFy8eJcmwUXqGGP3om6IxX8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79/go.mod h1:HKJDgKsFUnv5VAGeQjz8kxcgDP0HoE0iZNp0OdZNlhE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a h1:tPE/Kp+x9dMSwUm/uM0JKK0IfdiJkwAbSMSeZBXXJXc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
|
||||
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
|
||||
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
|
||||
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
|
||||
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
google.golang.org/api v0.228.0 h1:X2DJ/uoWGnY5obVjewbp8icSL5U4FzuCfy9OjbLSnLs=
|
||||
google.golang.org/api v0.228.0/go.mod h1:wNvRS1Pbe8r4+IfBIniV8fwCpGwTrYa+kMUDiC5z5a4=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20250324211829-b45e905df463 h1:qEFnJI6AnfZk0NNe8YTyXQh5i//Zxi4gBHwRgp76qpw=
|
||||
google.golang.org/genproto v0.0.0-20250324211829-b45e905df463/go.mod h1:SqIx1NV9hcvqdLHo7uNZDS5lrUJybQ3evo3+z/WBfA0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -569,20 +648,21 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
modernc.org/cc/v4 v4.26.3 h1:yEN8dzrkRFnn4PUUKXLYIqVf2PJYAEjMTFjO3BDGc3I=
|
||||
modernc.org/cc/v4 v4.26.3/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
|
||||
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
||||
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
|
||||
modernc.org/fileutil v1.3.15 h1:rJAXTP6ilMW/1+kzDiqmBlHLWszheUFXIyGQIAvjJpY=
|
||||
modernc.org/fileutil v1.3.15/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8=
|
||||
modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.66.7 h1:rjhZ8OSCybKWxS1CJr0hikpEi6Vg+944Ouyrd+bQsoY=
|
||||
modernc.org/libc v1.66.7/go.mod h1:ln6tbWX0NH+mzApEoDRvilBvAWFt1HX7AUA4VDdVDPM=
|
||||
modernc.org/libc v1.65.7 h1:Ia9Z4yzZtWNtUIuiPuQ7Qf7kxYrxP1/jeHZzG8bFu00=
|
||||
modernc.org/libc v1.65.7/go.mod h1:011EQibzzio/VX3ygj1qGFt5kMjP0lHb0qCW5/D/pQU=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
@@ -591,8 +671,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek=
|
||||
modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
|
||||
modernc.org/sqlite v1.37.1 h1:EgHJK/FPoqC+q2YBXg7fUmES37pCHFc97sI7zSayBEs=
|
||||
modernc.org/sqlite v1.37.1/go.mod h1:XwdRtsE1MpiBcL54+MbKcaDvcuej+IYSMfLN6gSKV8g=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@@ -38,10 +38,6 @@ func (svc *ItemService) Create(ctx Context, item repo.ItemCreate) (repo.ItemOut,
|
||||
return svc.repo.Items.Create(ctx, ctx.GID, item)
|
||||
}
|
||||
|
||||
func (svc *ItemService) Duplicate(ctx Context, gid, id uuid.UUID, options repo.DuplicateOptions) (repo.ItemOut, error) {
|
||||
return svc.repo.Items.Duplicate(ctx, gid, id, options)
|
||||
}
|
||||
|
||||
func (svc *ItemService) EnsureAssetID(ctx context.Context, gid uuid.UUID) (int, error) {
|
||||
items, err := svc.repo.Items.GetAllZeroAssetID(ctx, gid)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/item"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/sysadminsmedia/homebox/backend/pkgs/textutils"
|
||||
)
|
||||
|
||||
// AccentInsensitiveContains creates a predicate that performs accent-insensitive text search.
|
||||
// It normalizes both the database field value and the search value for comparison.
|
||||
func AccentInsensitiveContains(field string, searchValue string) predicate.Item {
|
||||
if searchValue == "" {
|
||||
return predicate.Item(func(s *sql.Selector) {
|
||||
// Return a predicate that never matches if search is empty
|
||||
s.Where(sql.False())
|
||||
})
|
||||
}
|
||||
|
||||
// Normalize the search value
|
||||
normalizedSearch := textutils.NormalizeSearchQuery(searchValue)
|
||||
|
||||
return predicate.Item(func(s *sql.Selector) {
|
||||
dialect := s.Dialect()
|
||||
|
||||
switch dialect {
|
||||
case "sqlite3":
|
||||
// For SQLite, we'll create a custom normalization function using REPLACE
|
||||
// to handle common accented characters
|
||||
normalizeFunc := buildSQLiteNormalizeExpression(s.C(field))
|
||||
s.Where(sql.ExprP(
|
||||
"LOWER("+normalizeFunc+") LIKE ?",
|
||||
"%"+normalizedSearch+"%",
|
||||
))
|
||||
case "postgres":
|
||||
// For PostgreSQL, use REPLACE-based normalization to avoid unaccent dependency
|
||||
normalizeFunc := buildGenericNormalizeExpression(s.C(field))
|
||||
// Use sql.P() for proper PostgreSQL parameter binding ($1, $2, etc.)
|
||||
s.Where(sql.P(func(b *sql.Builder) {
|
||||
b.WriteString("LOWER(")
|
||||
b.WriteString(normalizeFunc)
|
||||
b.WriteString(") LIKE ")
|
||||
b.Arg("%" + normalizedSearch + "%")
|
||||
}))
|
||||
default:
|
||||
// Default fallback using REPLACE for common accented characters
|
||||
normalizeFunc := buildGenericNormalizeExpression(s.C(field))
|
||||
s.Where(sql.ExprP(
|
||||
"LOWER("+normalizeFunc+") LIKE ?",
|
||||
"%"+normalizedSearch+"%",
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// buildSQLiteNormalizeExpression creates a SQLite expression to normalize accented characters
|
||||
func buildSQLiteNormalizeExpression(fieldExpr string) string {
|
||||
return buildGenericNormalizeExpression(fieldExpr)
|
||||
}
|
||||
|
||||
// buildGenericNormalizeExpression creates a database-agnostic expression to normalize common accented characters
|
||||
func buildGenericNormalizeExpression(fieldExpr string) string {
|
||||
// Chain REPLACE functions to handle the most common accented characters
|
||||
// Focused on the most frequently used accents in Spanish, French, and Portuguese
|
||||
// Ordered by frequency of use for better performance
|
||||
normalized := fieldExpr
|
||||
|
||||
// Most common accented characters ordered by frequency
|
||||
commonAccents := []struct {
|
||||
from, to string
|
||||
}{
|
||||
// Spanish - most common
|
||||
{"á", "a"}, {"é", "e"}, {"í", "i"}, {"ó", "o"}, {"ú", "u"}, {"ñ", "n"},
|
||||
{"Á", "A"}, {"É", "E"}, {"Í", "I"}, {"Ó", "O"}, {"Ú", "U"}, {"Ñ", "N"},
|
||||
|
||||
// French - most common
|
||||
{"è", "e"}, {"ê", "e"}, {"à", "a"}, {"ç", "c"},
|
||||
{"È", "E"}, {"Ê", "E"}, {"À", "A"}, {"Ç", "C"},
|
||||
|
||||
// German umlauts and Portuguese - common
|
||||
{"ä", "a"}, {"ö", "o"}, {"ü", "u"}, {"ã", "a"}, {"õ", "o"},
|
||||
{"Ä", "A"}, {"Ö", "O"}, {"Ü", "U"}, {"Ã", "A"}, {"Õ", "O"},
|
||||
}
|
||||
|
||||
for _, accent := range commonAccents {
|
||||
normalized = "REPLACE(" + normalized + ", '" + accent.from + "', '" + accent.to + "')"
|
||||
}
|
||||
|
||||
return normalized
|
||||
}
|
||||
|
||||
// ItemNameAccentInsensitiveContains creates an accent-insensitive search predicate for the item name field.
|
||||
func ItemNameAccentInsensitiveContains(value string) predicate.Item {
|
||||
return AccentInsensitiveContains(item.FieldName, value)
|
||||
}
|
||||
|
||||
// ItemDescriptionAccentInsensitiveContains creates an accent-insensitive search predicate for the item description field.
|
||||
func ItemDescriptionAccentInsensitiveContains(value string) predicate.Item {
|
||||
return AccentInsensitiveContains(item.FieldDescription, value)
|
||||
}
|
||||
|
||||
// ItemSerialNumberAccentInsensitiveContains creates an accent-insensitive search predicate for the item serial number field.
|
||||
func ItemSerialNumberAccentInsensitiveContains(value string) predicate.Item {
|
||||
return AccentInsensitiveContains(item.FieldSerialNumber, value)
|
||||
}
|
||||
|
||||
// ItemModelNumberAccentInsensitiveContains creates an accent-insensitive search predicate for the item model number field.
|
||||
func ItemModelNumberAccentInsensitiveContains(value string) predicate.Item {
|
||||
return AccentInsensitiveContains(item.FieldModelNumber, value)
|
||||
}
|
||||
|
||||
// ItemManufacturerAccentInsensitiveContains creates an accent-insensitive search predicate for the item manufacturer field.
|
||||
func ItemManufacturerAccentInsensitiveContains(value string) predicate.Item {
|
||||
return AccentInsensitiveContains(item.FieldManufacturer, value)
|
||||
}
|
||||
|
||||
// ItemNotesAccentInsensitiveContains creates an accent-insensitive search predicate for the item notes field.
|
||||
func ItemNotesAccentInsensitiveContains(value string) predicate.Item {
|
||||
return AccentInsensitiveContains(item.FieldNotes, value)
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBuildGenericNormalizeExpression(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "Simple field name",
|
||||
field: "name",
|
||||
expected: "name", // Should be wrapped in many REPLACE functions
|
||||
},
|
||||
{
|
||||
name: "Complex field name",
|
||||
field: "description",
|
||||
expected: "description",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := buildGenericNormalizeExpression(tt.field)
|
||||
|
||||
// Should contain the original field
|
||||
assert.Contains(t, result, tt.field)
|
||||
|
||||
// Should contain REPLACE functions for accent normalization
|
||||
assert.Contains(t, result, "REPLACE(")
|
||||
|
||||
// Should handle common accented characters
|
||||
assert.Contains(t, result, "'á'", "Should handle Spanish á")
|
||||
assert.Contains(t, result, "'é'", "Should handle Spanish é")
|
||||
assert.Contains(t, result, "'ñ'", "Should handle Spanish ñ")
|
||||
assert.Contains(t, result, "'ü'", "Should handle German ü")
|
||||
|
||||
// Should handle uppercase accents too
|
||||
assert.Contains(t, result, "'Á'", "Should handle uppercase Spanish Á")
|
||||
assert.Contains(t, result, "'É'", "Should handle uppercase Spanish É")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSQLiteNormalizeExpression(t *testing.T) {
|
||||
result := buildSQLiteNormalizeExpression("test_field")
|
||||
|
||||
// Should contain the field name and REPLACE functions
|
||||
assert.Contains(t, result, "test_field")
|
||||
assert.Contains(t, result, "REPLACE(")
|
||||
// Check for some specific accent replacements (order doesn't matter)
|
||||
assert.Contains(t, result, "'á'", "Should handle Spanish á")
|
||||
assert.Contains(t, result, "'ó'", "Should handle Spanish ó")
|
||||
}
|
||||
|
||||
func TestAccentInsensitivePredicateCreation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
searchValue string
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "Normal search value",
|
||||
field: "name",
|
||||
searchValue: "electronica",
|
||||
description: "Should create predicate for normal search",
|
||||
},
|
||||
{
|
||||
name: "Accented search value",
|
||||
field: "description",
|
||||
searchValue: "electrónica",
|
||||
description: "Should create predicate for accented search",
|
||||
},
|
||||
{
|
||||
name: "Empty search value",
|
||||
field: "name",
|
||||
searchValue: "",
|
||||
description: "Should handle empty search gracefully",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
predicate := AccentInsensitiveContains(tt.field, tt.searchValue)
|
||||
assert.NotNil(t, predicate, tt.description)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpecificItemPredicates(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
predicateFunc func(string) interface{}
|
||||
searchValue string
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "ItemNameAccentInsensitiveContains",
|
||||
predicateFunc: func(val string) interface{} { return ItemNameAccentInsensitiveContains(val) },
|
||||
searchValue: "electronica",
|
||||
description: "Should create accent-insensitive name search predicate",
|
||||
},
|
||||
{
|
||||
name: "ItemDescriptionAccentInsensitiveContains",
|
||||
predicateFunc: func(val string) interface{} { return ItemDescriptionAccentInsensitiveContains(val) },
|
||||
searchValue: "descripcion",
|
||||
description: "Should create accent-insensitive description search predicate",
|
||||
},
|
||||
{
|
||||
name: "ItemManufacturerAccentInsensitiveContains",
|
||||
predicateFunc: func(val string) interface{} { return ItemManufacturerAccentInsensitiveContains(val) },
|
||||
searchValue: "compañia",
|
||||
description: "Should create accent-insensitive manufacturer search predicate",
|
||||
},
|
||||
{
|
||||
name: "ItemSerialNumberAccentInsensitiveContains",
|
||||
predicateFunc: func(val string) interface{} { return ItemSerialNumberAccentInsensitiveContains(val) },
|
||||
searchValue: "sn123",
|
||||
description: "Should create accent-insensitive serial number search predicate",
|
||||
},
|
||||
{
|
||||
name: "ItemModelNumberAccentInsensitiveContains",
|
||||
predicateFunc: func(val string) interface{} { return ItemModelNumberAccentInsensitiveContains(val) },
|
||||
searchValue: "model456",
|
||||
description: "Should create accent-insensitive model number search predicate",
|
||||
},
|
||||
{
|
||||
name: "ItemNotesAccentInsensitiveContains",
|
||||
predicateFunc: func(val string) interface{} { return ItemNotesAccentInsensitiveContains(val) },
|
||||
searchValue: "notas importantes",
|
||||
description: "Should create accent-insensitive notes search predicate",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
predicate := tt.predicateFunc(tt.searchValue)
|
||||
assert.NotNil(t, predicate, tt.description)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,6 @@ package migrations
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
@@ -19,16 +17,15 @@ var sqliteFiles embed.FS
|
||||
// migration files in the binary at build time. The function takes a string
|
||||
// parameter "dialect" which specifies the SQL dialect to use. It returns an
|
||||
// embedded file system containing the migration files for the specified dialect.
|
||||
func Migrations(dialect string) (embed.FS, error) {
|
||||
func Migrations(dialect string) embed.FS {
|
||||
switch dialect {
|
||||
case "postgres":
|
||||
return postgresFiles, nil
|
||||
return postgresFiles
|
||||
case "sqlite3":
|
||||
return sqliteFiles, nil
|
||||
return sqliteFiles
|
||||
default:
|
||||
log.Error().Str("dialect", dialect).Msg("unknown sql dialect")
|
||||
return embed.FS{}, fmt.Errorf("unknown sql dialect: %s", dialect)
|
||||
log.Fatal().Str("dialect", dialect).Msg("unknown sql dialect")
|
||||
}
|
||||
// This should never get hit, but just in case
|
||||
return sqliteFiles, nil
|
||||
return sqliteFiles
|
||||
}
|
||||
|
||||
@@ -5,15 +5,6 @@ import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"image"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/evanoberholster/imagemeta"
|
||||
"github.com/gen2brain/avif"
|
||||
"github.com/gen2brain/heic"
|
||||
@@ -25,6 +16,13 @@ import (
|
||||
"github.com/sysadminsmedia/homebox/backend/pkgs/utils"
|
||||
"github.com/zeebo/blake3"
|
||||
"golang.org/x/image/draw"
|
||||
"image"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent"
|
||||
@@ -102,30 +100,13 @@ func (r *AttachmentRepo) path(gid uuid.UUID, hash string) string {
|
||||
}
|
||||
|
||||
func (r *AttachmentRepo) GetConnString() string {
|
||||
// Handle the default case for file storage
|
||||
// which is file:///./ meaning relative to the current working directory
|
||||
if strings.HasPrefix(r.storage.ConnString, "file:///./") {
|
||||
dir, err := filepath.Abs(strings.TrimPrefix(r.storage.ConnString, "file:///./"))
|
||||
if runtime.GOOS == "windows" {
|
||||
dir = fmt.Sprintf("/%s", dir)
|
||||
}
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to get absolute path for attachment directory")
|
||||
return r.storage.ConnString
|
||||
}
|
||||
return strings.ReplaceAll(fmt.Sprintf("file://%s?no_tmp_dir=true", dir), "\\", "/")
|
||||
} else if strings.HasPrefix(r.storage.ConnString, "file://") {
|
||||
// Handle the case for file storage with an absolute path
|
||||
// Convert Windows paths to a format compatible with fileblob
|
||||
// e.g. file:///C:/path/to/file becomes file:///C/path
|
||||
dir := strings.TrimPrefix(strings.ReplaceAll(r.storage.ConnString, "\\", "/"), "file://")
|
||||
if runtime.GOOS == "windows" {
|
||||
// Remove the colon from the drive letter (in case the user adds it)
|
||||
dir = strings.ReplaceAll(dir, ":", "")
|
||||
// Ensure the path starts with a slash for Windows compatibility
|
||||
dir = fmt.Sprintf("/%s", dir)
|
||||
}
|
||||
return fmt.Sprintf("file://%s", dir)
|
||||
return fmt.Sprintf("file://%s?no_tmp_dir=true", dir)
|
||||
}
|
||||
return r.storage.ConnString
|
||||
}
|
||||
@@ -338,19 +319,16 @@ func (r *AttachmentRepo) Update(ctx context.Context, gid uuid.UUID, id uuid.UUID
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Only remove primary status from other photo attachments when setting a new photo as primary
|
||||
if typ == attachment.TypePhoto && data.Primary {
|
||||
err = r.db.Attachment.Update().
|
||||
Where(
|
||||
attachment.HasItemWith(item.ID(attachmentItem.ID)),
|
||||
attachment.IDNEQ(updatedAttachment.ID),
|
||||
attachment.TypeEQ(attachment.TypePhoto),
|
||||
).
|
||||
SetPrimary(false).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Ensure all other attachments are not primary
|
||||
err = r.db.Attachment.Update().
|
||||
Where(
|
||||
attachment.HasItemWith(item.ID(attachmentItem.ID)),
|
||||
attachment.IDNEQ(updatedAttachment.ID),
|
||||
).
|
||||
SetPrimary(false).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.Get(ctx, gid, updatedAttachment.ID)
|
||||
|
||||
@@ -152,132 +152,3 @@ func TestAttachmentRepo_EnsureSinglePrimaryAttachment(t *testing.T) {
|
||||
setAndVerifyPrimary(attachments[0].ID, attachments[1].ID)
|
||||
setAndVerifyPrimary(attachments[1].ID, attachments[0].ID)
|
||||
}
|
||||
|
||||
func TestAttachmentRepo_UpdateNonPhotoDoesNotAffectPrimaryPhoto(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
item := useItems(t, 1)[0]
|
||||
|
||||
// Create a photo attachment that will be primary
|
||||
photoAttachment, err := tRepos.Attachments.Create(ctx, item.ID, ItemCreateAttachment{Title: "Test Photo", Content: strings.NewReader("Photo content")}, attachment.TypePhoto, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a manual attachment (non-photo)
|
||||
manualAttachment, err := tRepos.Attachments.Create(ctx, item.ID, ItemCreateAttachment{Title: "Test Manual", Content: strings.NewReader("Manual content")}, attachment.TypeManual, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Cleanup
|
||||
t.Cleanup(func() {
|
||||
_ = tRepos.Attachments.Delete(ctx, tGroup.ID, item.ID, photoAttachment.ID)
|
||||
_ = tRepos.Attachments.Delete(ctx, tGroup.ID, item.ID, manualAttachment.ID)
|
||||
})
|
||||
|
||||
// Verify photo is primary initially
|
||||
photoAttachment, err = tRepos.Attachments.Get(ctx, tGroup.ID, photoAttachment.ID)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, photoAttachment.Primary)
|
||||
|
||||
// Update the manual attachment (this should NOT affect the photo's primary status)
|
||||
_, err = tRepos.Attachments.Update(ctx, tGroup.ID, manualAttachment.ID, &ItemAttachmentUpdate{
|
||||
Type: attachment.TypeManual.String(),
|
||||
Title: "Updated Manual",
|
||||
Primary: false, // This should have no effect since it's not a photo
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify photo is still primary after updating the manual
|
||||
photoAttachment, err = tRepos.Attachments.Get(ctx, tGroup.ID, photoAttachment.ID)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, photoAttachment.Primary, "Photo attachment should remain primary after updating non-photo attachment")
|
||||
|
||||
// Verify manual attachment is not primary
|
||||
manualAttachment, err = tRepos.Attachments.Get(ctx, tGroup.ID, manualAttachment.ID)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, manualAttachment.Primary)
|
||||
}
|
||||
|
||||
func TestAttachmentRepo_AddingPDFAfterPhotoKeepsPhotoAsPrimary(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
item := useItems(t, 1)[0]
|
||||
|
||||
// Step 1: Upload a photo first (this should become primary since it's the first photo)
|
||||
photoAttachment, err := tRepos.Attachments.Create(ctx, item.ID, ItemCreateAttachment{Title: "Item Photo", Content: strings.NewReader("Photo content")}, attachment.TypePhoto, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Cleanup
|
||||
t.Cleanup(func() {
|
||||
_ = tRepos.Attachments.Delete(ctx, tGroup.ID, item.ID, photoAttachment.ID)
|
||||
})
|
||||
|
||||
// Verify photo becomes primary automatically (since it's the first photo)
|
||||
photoAttachment, err = tRepos.Attachments.Get(ctx, tGroup.ID, photoAttachment.ID)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, photoAttachment.Primary, "First photo should automatically become primary")
|
||||
|
||||
// Step 2: Add a PDF receipt (this should NOT affect the photo's primary status)
|
||||
pdfAttachment, err := tRepos.Attachments.Create(ctx, item.ID, ItemCreateAttachment{Title: "Receipt PDF", Content: strings.NewReader("PDF content")}, attachment.TypeReceipt, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add to cleanup
|
||||
t.Cleanup(func() {
|
||||
_ = tRepos.Attachments.Delete(ctx, tGroup.ID, item.ID, pdfAttachment.ID)
|
||||
})
|
||||
|
||||
// Step 3: Verify photo is still primary after adding PDF
|
||||
photoAttachment, err = tRepos.Attachments.Get(ctx, tGroup.ID, photoAttachment.ID)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, photoAttachment.Primary, "Photo should remain primary after adding PDF attachment")
|
||||
|
||||
// Verify PDF is not primary
|
||||
pdfAttachment, err = tRepos.Attachments.Get(ctx, tGroup.ID, pdfAttachment.ID)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, pdfAttachment.Primary)
|
||||
|
||||
// Step 4: Test the actual item summary mapping (this is what determines the card display)
|
||||
updatedItem, err := tRepos.Items.GetOne(ctx, item.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// The item should have the photo's ID as the imageId
|
||||
assert.NotNil(t, updatedItem.ImageID, "Item should have an imageId")
|
||||
assert.Equal(t, photoAttachment.ID, *updatedItem.ImageID, "Item's imageId should match the photo attachment ID")
|
||||
}
|
||||
|
||||
func TestAttachmentRepo_SettingPhotoPrimaryStillWorks(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
item := useItems(t, 1)[0]
|
||||
|
||||
// Create two photo attachments
|
||||
photo1, err := tRepos.Attachments.Create(ctx, item.ID, ItemCreateAttachment{Title: "Photo 1", Content: strings.NewReader("Photo 1 content")}, attachment.TypePhoto, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
photo2, err := tRepos.Attachments.Create(ctx, item.ID, ItemCreateAttachment{Title: "Photo 2", Content: strings.NewReader("Photo 2 content")}, attachment.TypePhoto, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Cleanup
|
||||
t.Cleanup(func() {
|
||||
_ = tRepos.Attachments.Delete(ctx, tGroup.ID, item.ID, photo1.ID)
|
||||
_ = tRepos.Attachments.Delete(ctx, tGroup.ID, item.ID, photo2.ID)
|
||||
})
|
||||
|
||||
// First photo should be primary (since it was created first)
|
||||
photo1, err = tRepos.Attachments.Get(ctx, tGroup.ID, photo1.ID)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, photo1.Primary)
|
||||
|
||||
photo2, err = tRepos.Attachments.Get(ctx, tGroup.ID, photo2.ID)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, photo2.Primary)
|
||||
|
||||
// Now set photo2 as primary (this should work and remove primary from photo1)
|
||||
photo2, err = tRepos.Attachments.Update(ctx, tGroup.ID, photo2.ID, &ItemAttachmentUpdate{
|
||||
Type: attachment.TypePhoto.String(),
|
||||
Title: "Photo 2",
|
||||
Primary: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.True(t, photo2.Primary)
|
||||
|
||||
// Verify photo1 is no longer primary
|
||||
photo1, err = tRepos.Attachments.Get(ctx, tGroup.ID, photo1.ID)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, photo1.Primary, "Photo 1 should no longer be primary after setting Photo 2 as primary")
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services/reporting/eventbus"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/attachment"
|
||||
@@ -15,7 +14,6 @@ import (
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/itemfield"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/label"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/location"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/maintenanceentry"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/types"
|
||||
)
|
||||
@@ -48,13 +46,6 @@ type (
|
||||
OrderBy string `json:"orderBy"`
|
||||
}
|
||||
|
||||
DuplicateOptions struct {
|
||||
CopyMaintenance bool `json:"copyMaintenance"`
|
||||
CopyAttachments bool `json:"copyAttachments"`
|
||||
CopyCustomFields bool `json:"copyCustomFields"`
|
||||
CopyPrefix string `json:"copyPrefix"`
|
||||
}
|
||||
|
||||
ItemField struct {
|
||||
ID uuid.UUID `json:"id,omitempty"`
|
||||
Type string `json:"type"`
|
||||
@@ -369,25 +360,14 @@ func (e *ItemsRepository) QueryByGroup(ctx context.Context, gid uuid.UUID, q Ite
|
||||
}
|
||||
|
||||
if q.Search != "" {
|
||||
// Use accent-insensitive search predicates that normalize both
|
||||
// the search query and database field values during comparison.
|
||||
// For queries without accents, the traditional search is more efficient.
|
||||
qb.Where(
|
||||
item.Or(
|
||||
// Regular case-insensitive search (fastest)
|
||||
item.NameContainsFold(q.Search),
|
||||
item.DescriptionContainsFold(q.Search),
|
||||
item.SerialNumberContainsFold(q.Search),
|
||||
item.ModelNumberContainsFold(q.Search),
|
||||
item.ManufacturerContainsFold(q.Search),
|
||||
item.NotesContainsFold(q.Search),
|
||||
// Accent-insensitive search using custom predicates
|
||||
ent.ItemNameAccentInsensitiveContains(q.Search),
|
||||
ent.ItemDescriptionAccentInsensitiveContains(q.Search),
|
||||
ent.ItemSerialNumberAccentInsensitiveContains(q.Search),
|
||||
ent.ItemModelNumberAccentInsensitiveContains(q.Search),
|
||||
ent.ItemManufacturerAccentInsensitiveContains(q.Search),
|
||||
ent.ItemNotesAccentInsensitiveContains(q.Search),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -1013,164 +993,3 @@ func (e *ItemsRepository) SetPrimaryPhotos(ctx context.Context, gid uuid.UUID) (
|
||||
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
// Duplicate creates a copy of an item with configurable options for what data to copy.
|
||||
// The new item will have the next available asset ID and a customizable prefix in the name.
|
||||
func (e *ItemsRepository) Duplicate(ctx context.Context, gid, id uuid.UUID, options DuplicateOptions) (ItemOut, error) {
|
||||
tx, err := e.db.Tx(ctx)
|
||||
if err != nil {
|
||||
return ItemOut{}, err
|
||||
}
|
||||
committed := false
|
||||
defer func() {
|
||||
if !committed {
|
||||
if err := tx.Rollback(); err != nil {
|
||||
log.Warn().Err(err).Msg("failed to rollback transaction during item duplication")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Get the original item with all its data
|
||||
originalItem, err := e.getOne(ctx, item.ID(id), item.HasGroupWith(group.ID(gid)))
|
||||
if err != nil {
|
||||
return ItemOut{}, err
|
||||
}
|
||||
|
||||
nextAssetID, err := e.GetHighestAssetID(ctx, gid)
|
||||
if err != nil {
|
||||
return ItemOut{}, err
|
||||
}
|
||||
nextAssetID++
|
||||
|
||||
// Set default copy prefix if not provided
|
||||
if options.CopyPrefix == "" {
|
||||
options.CopyPrefix = "Copy of "
|
||||
}
|
||||
|
||||
// Create the new item directly in the transaction
|
||||
newItemID := uuid.New()
|
||||
itemBuilder := tx.Item.Create().
|
||||
SetID(newItemID).
|
||||
SetName(options.CopyPrefix + originalItem.Name).
|
||||
SetDescription(originalItem.Description).
|
||||
SetQuantity(originalItem.Quantity).
|
||||
SetLocationID(originalItem.Location.ID).
|
||||
SetGroupID(gid).
|
||||
SetAssetID(int(nextAssetID)).
|
||||
SetSerialNumber(originalItem.SerialNumber).
|
||||
SetModelNumber(originalItem.ModelNumber).
|
||||
SetManufacturer(originalItem.Manufacturer).
|
||||
SetLifetimeWarranty(originalItem.LifetimeWarranty).
|
||||
SetWarrantyExpires(originalItem.WarrantyExpires.Time()).
|
||||
SetWarrantyDetails(originalItem.WarrantyDetails).
|
||||
SetPurchaseTime(originalItem.PurchaseTime.Time()).
|
||||
SetPurchaseFrom(originalItem.PurchaseFrom).
|
||||
SetPurchasePrice(originalItem.PurchasePrice).
|
||||
SetSoldTime(originalItem.SoldTime.Time()).
|
||||
SetSoldTo(originalItem.SoldTo).
|
||||
SetSoldPrice(originalItem.SoldPrice).
|
||||
SetSoldNotes(originalItem.SoldNotes).
|
||||
SetNotes(originalItem.Notes).
|
||||
SetInsured(originalItem.Insured).
|
||||
SetArchived(originalItem.Archived).
|
||||
SetSyncChildItemsLocations(originalItem.SyncChildItemsLocations)
|
||||
|
||||
if originalItem.Parent != nil {
|
||||
itemBuilder.SetParentID(originalItem.Parent.ID)
|
||||
}
|
||||
|
||||
// Add labels
|
||||
if len(originalItem.Labels) > 0 {
|
||||
labelIDs := make([]uuid.UUID, len(originalItem.Labels))
|
||||
for i, label := range originalItem.Labels {
|
||||
labelIDs[i] = label.ID
|
||||
}
|
||||
itemBuilder.AddLabelIDs(labelIDs...)
|
||||
}
|
||||
|
||||
_, err = itemBuilder.Save(ctx)
|
||||
if err != nil {
|
||||
return ItemOut{}, err
|
||||
}
|
||||
|
||||
// Copy custom fields if requested
|
||||
if options.CopyCustomFields {
|
||||
for _, field := range originalItem.Fields {
|
||||
_, err = tx.ItemField.Create().
|
||||
SetItemID(newItemID).
|
||||
SetType(itemfield.Type(field.Type)).
|
||||
SetName(field.Name).
|
||||
SetTextValue(field.TextValue).
|
||||
SetNumberValue(field.NumberValue).
|
||||
SetBooleanValue(field.BooleanValue).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("field_name", field.Name).Msg("failed to copy custom field during duplication")
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy attachments if requested
|
||||
if options.CopyAttachments {
|
||||
for _, att := range originalItem.Attachments {
|
||||
// Get the original attachment file
|
||||
originalAttachment, err := tx.Attachment.Query().
|
||||
Where(attachment.ID(att.ID)).
|
||||
Only(ctx)
|
||||
if err != nil {
|
||||
// Log error but continue to copy other attachments
|
||||
log.Warn().Err(err).Str("attachment_id", att.ID.String()).Msg("failed to find attachment during duplication")
|
||||
continue
|
||||
}
|
||||
|
||||
// Create a copy of the attachment with the same file path
|
||||
// Since files are stored with hash-based paths, this is safe
|
||||
_, err = tx.Attachment.Create().
|
||||
SetItemID(newItemID).
|
||||
SetType(originalAttachment.Type).
|
||||
SetTitle(originalAttachment.Title).
|
||||
SetPath(originalAttachment.Path).
|
||||
SetMimeType(originalAttachment.MimeType).
|
||||
SetPrimary(originalAttachment.Primary).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("original_attachment_id", att.ID.String()).Msg("failed to copy attachment during duplication")
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy maintenance entries if requested
|
||||
if options.CopyMaintenance {
|
||||
maintenanceEntries, err := tx.MaintenanceEntry.Query().
|
||||
Where(maintenanceentry.HasItemWith(item.ID(id))).
|
||||
All(ctx)
|
||||
if err == nil {
|
||||
for _, entry := range maintenanceEntries {
|
||||
_, err = tx.MaintenanceEntry.Create().
|
||||
SetItemID(newItemID).
|
||||
SetDate(entry.Date).
|
||||
SetScheduledDate(entry.ScheduledDate).
|
||||
SetName(entry.Name).
|
||||
SetDescription(entry.Description).
|
||||
SetCost(entry.Cost).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("maintenance_entry_id", entry.ID.String()).Msg("failed to copy maintenance entry during duplication")
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return ItemOut{}, err
|
||||
}
|
||||
committed = true
|
||||
|
||||
e.publishMutationEvent(gid)
|
||||
|
||||
// Get the final item with all copied data
|
||||
return e.GetOne(ctx, newItemID)
|
||||
}
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/sysadminsmedia/homebox/backend/pkgs/textutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestItemsRepository_AccentInsensitiveSearch(t *testing.T) {
|
||||
// Test cases for accent-insensitive search
|
||||
testCases := []struct {
|
||||
name string
|
||||
itemName string
|
||||
searchQuery string
|
||||
shouldMatch bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "Spanish accented item, search without accents",
|
||||
itemName: "electrónica",
|
||||
searchQuery: "electronica",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'electrónica' when searching for 'electronica'",
|
||||
},
|
||||
{
|
||||
name: "Spanish accented item, search with accents",
|
||||
itemName: "electrónica",
|
||||
searchQuery: "electrónica",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'electrónica' when searching for 'electrónica'",
|
||||
},
|
||||
{
|
||||
name: "Non-accented item, search with accents",
|
||||
itemName: "electronica",
|
||||
searchQuery: "electrónica",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'electronica' when searching for 'electrónica' (bidirectional search)",
|
||||
},
|
||||
{
|
||||
name: "Spanish item with tilde, search without accents",
|
||||
itemName: "café",
|
||||
searchQuery: "cafe",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'café' when searching for 'cafe'",
|
||||
},
|
||||
{
|
||||
name: "Spanish item without tilde, search with accents",
|
||||
itemName: "cafe",
|
||||
searchQuery: "café",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'cafe' when searching for 'café' (bidirectional)",
|
||||
},
|
||||
{
|
||||
name: "French accented item, search without accents",
|
||||
itemName: "pére",
|
||||
searchQuery: "pere",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'pére' when searching for 'pere'",
|
||||
},
|
||||
{
|
||||
name: "French: père without accent, search with accents",
|
||||
itemName: "pere",
|
||||
searchQuery: "père",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'pere' when searching for 'père' (bidirectional)",
|
||||
},
|
||||
{
|
||||
name: "Mixed case with accents",
|
||||
itemName: "Electrónica",
|
||||
searchQuery: "ELECTRONICA",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'Electrónica' when searching for 'ELECTRONICA' (case insensitive)",
|
||||
},
|
||||
{
|
||||
name: "Bidirectional: Non-accented item, search with different accents",
|
||||
itemName: "cafe",
|
||||
searchQuery: "café",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'cafe' when searching for 'café' (bidirectional)",
|
||||
},
|
||||
{
|
||||
name: "Bidirectional: Item with accent, search with different accent",
|
||||
itemName: "résumé",
|
||||
searchQuery: "resume",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'résumé' when searching for 'resume' (bidirectional)",
|
||||
},
|
||||
{
|
||||
name: "Bidirectional: Spanish ñ to n",
|
||||
itemName: "espanol",
|
||||
searchQuery: "español",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'espanol' when searching for 'español' (bidirectional ñ)",
|
||||
},
|
||||
{
|
||||
name: "French: français with accent, search without",
|
||||
itemName: "français",
|
||||
searchQuery: "francais",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'français' when searching for 'francais'",
|
||||
},
|
||||
{
|
||||
name: "French: français without accent, search with",
|
||||
itemName: "francais",
|
||||
searchQuery: "français",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'francais' when searching for 'français' (bidirectional)",
|
||||
},
|
||||
{
|
||||
name: "French: été with accent, search without",
|
||||
itemName: "été",
|
||||
searchQuery: "ete",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'été' when searching for 'ete'",
|
||||
},
|
||||
{
|
||||
name: "French: été without accent, search with",
|
||||
itemName: "ete",
|
||||
searchQuery: "été",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'ete' when searching for 'été' (bidirectional)",
|
||||
},
|
||||
{
|
||||
name: "French: hôtel with accent, search without",
|
||||
itemName: "hôtel",
|
||||
searchQuery: "hotel",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'hôtel' when searching for 'hotel'",
|
||||
},
|
||||
{
|
||||
name: "French: hôtel without accent, search with",
|
||||
itemName: "hotel",
|
||||
searchQuery: "hôtel",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'hotel' when searching for 'hôtel' (bidirectional)",
|
||||
},
|
||||
{
|
||||
name: "French: naïve with accent, search without",
|
||||
itemName: "naïve",
|
||||
searchQuery: "naive",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'naïve' when searching for 'naive'",
|
||||
},
|
||||
{
|
||||
name: "French: naïve without accent, search with",
|
||||
itemName: "naive",
|
||||
searchQuery: "naïve",
|
||||
shouldMatch: true,
|
||||
description: "Should find 'naive' when searching for 'naïve' (bidirectional)",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Test the normalization logic used in the repository
|
||||
normalizedSearch := textutils.NormalizeSearchQuery(tc.searchQuery)
|
||||
|
||||
// This simulates what happens in the repository
|
||||
// The original search would find exact matches (case-insensitive)
|
||||
// The normalized search would find accent-insensitive matches
|
||||
|
||||
// Test that our normalization works as expected
|
||||
if tc.shouldMatch {
|
||||
// If it should match, then either the original query should match
|
||||
// or the normalized query should match when applied to the stored data
|
||||
assert.NotEqual(t, "", normalizedSearch, "Normalized search should not be empty")
|
||||
|
||||
// The key insight is that we're searching with both the original and normalized queries
|
||||
// So "electrónica" will be found when searching for "electronica" because:
|
||||
// 1. Original search: "electronica" doesn't match "electrónica"
|
||||
// 2. Normalized search: "electronica" matches the normalized version
|
||||
t.Logf("✓ %s: Item '%s' should be found with search '%s' (normalized: '%s')",
|
||||
tc.description, tc.itemName, tc.searchQuery, normalizedSearch)
|
||||
} else {
|
||||
t.Logf("✗ %s: Item '%s' should NOT be found with search '%s' (normalized: '%s')",
|
||||
tc.description, tc.itemName, tc.searchQuery, normalizedSearch)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeSearchQueryIntegration(t *testing.T) {
|
||||
// Test that the normalization function works correctly
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"electrónica", "electronica"},
|
||||
{"café", "cafe"},
|
||||
{"ELECTRÓNICA", "electronica"},
|
||||
{"Café París", "cafe paris"},
|
||||
{"hello world", "hello world"},
|
||||
// French accented words
|
||||
{"père", "pere"},
|
||||
{"français", "francais"},
|
||||
{"été", "ete"},
|
||||
{"hôtel", "hotel"},
|
||||
{"naïve", "naive"},
|
||||
{"PÈRE", "pere"},
|
||||
{"FRANÇAIS", "francais"},
|
||||
{"ÉTÉ", "ete"},
|
||||
{"HÔTEL", "hotel"},
|
||||
{"NAÏVE", "naive"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.input, func(t *testing.T) {
|
||||
result := textutils.NormalizeSearchQuery(tc.input)
|
||||
assert.Equal(t, tc.expected, result, "Normalization should work correctly")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -20,14 +20,14 @@ type LabelRepository struct {
|
||||
type (
|
||||
LabelCreate struct {
|
||||
Name string `json:"name" validate:"required,min=1,max=255"`
|
||||
Description string `json:"description" validate:"max=1000"`
|
||||
Description string `json:"description" validate:"max=255"`
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
||||
LabelUpdate struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Name string `json:"name" validate:"required,min=1,max=255"`
|
||||
Description string `json:"description" validate:"max=1000"`
|
||||
Description string `json:"description" validate:"max=255"`
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package repo
|
||||
|
||||
type BarcodeProduct struct {
|
||||
SearchEngineName string `json:"search_engine_name"`
|
||||
|
||||
// Identifications
|
||||
ModelNumber string `json:"modelNumber"`
|
||||
Manufacturer string `json:"manufacturer"`
|
||||
|
||||
// Extras
|
||||
Country string `json:"notes"`
|
||||
Barcode string `json:"barcode"`
|
||||
|
||||
ImageURL string `json:"imageURL"`
|
||||
ImageBase64 string `json:"imageBase64"`
|
||||
|
||||
Item ItemCreate `json:"item"`
|
||||
}
|
||||
@@ -11,8 +11,6 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var startTime = time.Now()
|
||||
|
||||
type Data struct {
|
||||
Domain string `json:"domain"`
|
||||
Name string `json:"name"`
|
||||
@@ -20,7 +18,7 @@ type Data struct {
|
||||
Props map[string]interface{} `json:"props"`
|
||||
}
|
||||
|
||||
func Send(version, buildInfo string) error {
|
||||
func Send(version, buildInfo string) {
|
||||
hostData, _ := host.Info()
|
||||
analytics := Data{
|
||||
Domain: "homebox.software",
|
||||
@@ -34,23 +32,22 @@ func Send(version, buildInfo string) error {
|
||||
"platform_version": hostData.PlatformVersion,
|
||||
"kernel_arch": hostData.KernelArch,
|
||||
"virt_type": hostData.VirtualizationSystem,
|
||||
"uptime_min": time.Since(startTime).Minutes(),
|
||||
},
|
||||
}
|
||||
jsonBody, err := json.Marshal(analytics)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to marshal analytics data")
|
||||
return err
|
||||
return
|
||||
}
|
||||
bodyReader := bytes.NewReader(jsonBody)
|
||||
req, err := http.NewRequest("POST", "https://a.sysadmins.zone/api/event", bodyReader)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to create analytics request")
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", "Homebox/"+version+"/(https://homebox.software)")
|
||||
req.Header.Set("User-Agent", "Homebox/"+version+"/"+buildInfo+" (https://homebox.software)")
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
@@ -59,7 +56,7 @@ func Send(version, buildInfo string) error {
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to send analytics request")
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
@@ -68,5 +65,4 @@ func Send(version, buildInfo string) error {
|
||||
log.Error().Err(err).Msg("failed to close response body")
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ type Config struct {
|
||||
Options Options `yaml:"options"`
|
||||
LabelMaker LabelMakerConf `yaml:"labelmaker"`
|
||||
Thumbnail Thumbnail `yaml:"thumbnail"`
|
||||
Barcode BarcodeAPIConf `yaml:"barcode"`
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
@@ -61,20 +60,14 @@ type WebConfig struct {
|
||||
}
|
||||
|
||||
type LabelMakerConf struct {
|
||||
Width int64 `yaml:"width" conf:"default:526"`
|
||||
Height int64 `yaml:"height" conf:"default:200"`
|
||||
Padding int64 `yaml:"padding" conf:"default:32"`
|
||||
Margin int64 `yaml:"margin" conf:"default:32"`
|
||||
FontSize float64 `yaml:"font_size" conf:"default:32.0"`
|
||||
PrintCommand *string `yaml:"string"`
|
||||
AdditionalInformation *string `yaml:"string"`
|
||||
DynamicLength bool `yaml:"bool" conf:"default:true"`
|
||||
LabelServiceUrl *string `yaml:"label_service_url"`
|
||||
LabelServiceTimeout *time.Duration `yaml:"label_service_timeout"`
|
||||
}
|
||||
|
||||
type BarcodeAPIConf struct {
|
||||
TokenBarcodespider string `yaml:"token_barcodespider"`
|
||||
Width int64 `yaml:"width" conf:"default:526"`
|
||||
Height int64 `yaml:"height" conf:"default:200"`
|
||||
Padding int64 `yaml:"padding" conf:"default:32"`
|
||||
Margin int64 `yaml:"margin" conf:"default:32"`
|
||||
FontSize float64 `yaml:"font_size" conf:"default:32.0"`
|
||||
PrintCommand *string `yaml:"string"`
|
||||
AdditionalInformation *string `yaml:"string"`
|
||||
DynamicLength bool `yaml:"bool" conf:"default:true"`
|
||||
}
|
||||
|
||||
// New parses the CLI/Config file and returns a Config struct. If the file argument is an empty string, the
|
||||
|
||||
@@ -17,10 +17,7 @@ type Database struct {
|
||||
Host string `yaml:"host"`
|
||||
Port string `yaml:"port"`
|
||||
Database string `yaml:"database"`
|
||||
SslMode string `yaml:"ssl_mode" conf:"default:prefer"`
|
||||
SslRootCert string `yaml:"ssl_rootcert"`
|
||||
SslCert string `yaml:"ssl_cert"`
|
||||
SslKey string `yaml:"ssl_key"`
|
||||
SslMode string `yaml:"ssl_mode"`
|
||||
SqlitePath string `yaml:"sqlite_path" conf:"default:./.data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1&_time_format=sqlite"`
|
||||
PubSubConnString string `yaml:"pubsub_conn_string" conf:"default:mem://{{ .Topic }}"`
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ import (
|
||||
"image/png"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -140,18 +138,11 @@ func wrapText(text string, face font.Face, maxWidth int, maxHeight int, lineHeig
|
||||
return wrappedLines, ""
|
||||
}
|
||||
|
||||
func GenerateLabel(w io.Writer, params *GenerateParameters, cfg *config.Config) error {
|
||||
func GenerateLabel(w io.Writer, params *GenerateParameters) error {
|
||||
if err := params.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If LabelServiceUrl is configured, fetch the label from the URL instead of generating it
|
||||
if cfg != nil && cfg.LabelMaker.LabelServiceUrl != nil && *cfg.LabelMaker.LabelServiceUrl != "" {
|
||||
log.Printf("LabelServiceUrl configured: %s", *cfg.LabelMaker.LabelServiceUrl)
|
||||
|
||||
return fetchLabelFromURL(w, *cfg.LabelMaker.LabelServiceUrl, params, cfg)
|
||||
}
|
||||
|
||||
bodyText := params.DescriptionText
|
||||
if params.AdditionalInformation != nil {
|
||||
bodyText = bodyText + "\n" + *params.AdditionalInformation
|
||||
@@ -227,7 +218,7 @@ func GenerateLabel(w io.Writer, params *GenerateParameters, cfg *config.Config)
|
||||
// Create the actual image with calculated height
|
||||
bounds := image.Rect(0, 0, params.Width, requiredHeight)
|
||||
img := image.NewRGBA(bounds)
|
||||
draw.Draw(img, bounds, &image.Uniform{C: color.White}, image.Point{}, draw.Src)
|
||||
draw.Draw(img, bounds, &image.Uniform{color.White}, image.Point{}, draw.Src)
|
||||
|
||||
// Draw QR code onto the image
|
||||
draw.Draw(img,
|
||||
@@ -288,98 +279,6 @@ func createContext(font *truetype.Font, size float64, img *image.RGBA, dpi float
|
||||
return c
|
||||
}
|
||||
|
||||
// fetchLabelFromURL fetches an image from the specified URL and writes it to the writer
|
||||
func fetchLabelFromURL(w io.Writer, serviceURL string, params *GenerateParameters, cfg *config.Config) error {
|
||||
// Parse the base URL
|
||||
baseURL, err := url.Parse(serviceURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse service URL %s: %w", serviceURL, err)
|
||||
}
|
||||
|
||||
// Build query parameters with the same attributes passed to print command
|
||||
query := url.Values{}
|
||||
query.Set("Width", fmt.Sprintf("%d", params.Width))
|
||||
query.Set("Height", fmt.Sprintf("%d", params.Height))
|
||||
query.Set("QrSize", fmt.Sprintf("%d", params.QrSize))
|
||||
query.Set("Margin", fmt.Sprintf("%d", params.Margin))
|
||||
query.Set("ComponentPadding", fmt.Sprintf("%d", params.ComponentPadding))
|
||||
query.Set("TitleText", params.TitleText)
|
||||
query.Set("TitleFontSize", fmt.Sprintf("%f", params.TitleFontSize))
|
||||
query.Set("DescriptionText", params.DescriptionText)
|
||||
query.Set("DescriptionFontSize", fmt.Sprintf("%f", params.DescriptionFontSize))
|
||||
query.Set("Dpi", fmt.Sprintf("%f", params.Dpi))
|
||||
query.Set("URL", params.URL)
|
||||
query.Set("DynamicLength", fmt.Sprintf("%t", params.DynamicLength))
|
||||
|
||||
// Add AdditionalInformation if it exists
|
||||
if params.AdditionalInformation != nil {
|
||||
query.Set("AdditionalInformation", *params.AdditionalInformation)
|
||||
}
|
||||
|
||||
// Set the query parameters
|
||||
baseURL.RawQuery = query.Encode()
|
||||
finalServiceURL := baseURL.String()
|
||||
|
||||
log.Printf("Fetching label from URL: %s", finalServiceURL)
|
||||
|
||||
// Use configured timeout or default to 30 seconds
|
||||
timeout := 30 * time.Second
|
||||
if cfg != nil && cfg.LabelMaker.LabelServiceTimeout != nil {
|
||||
timeout = *cfg.LabelMaker.LabelServiceTimeout
|
||||
}
|
||||
|
||||
// Create HTTP client with configurable timeout
|
||||
client := &http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
// Create HTTP request with custom headers
|
||||
req, err := http.NewRequest("GET", finalServiceURL, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create request for URL %s: %w", finalServiceURL, err)
|
||||
}
|
||||
|
||||
// Set custom headers
|
||||
req.Header.Set("User-Agent", "Homebox-LabelMaker/1.0")
|
||||
req.Header.Set("Accept", "image/*")
|
||||
|
||||
// Make HTTP request to the label service
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch label from URL %s: %w", finalServiceURL, err)
|
||||
}
|
||||
|
||||
// Check if the response status is OK
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("label service returned status %d for URL %s", resp.StatusCode, finalServiceURL)
|
||||
}
|
||||
|
||||
// Check if the response is an image
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
if !strings.HasPrefix(contentType, "image/") {
|
||||
return fmt.Errorf("label service returned invalid content type %s, expected image/*", contentType)
|
||||
}
|
||||
|
||||
// Set default max response size (10MB)
|
||||
maxResponseSize := int64(10 << 20)
|
||||
if cfg != nil {
|
||||
maxResponseSize = cfg.Web.MaxUploadSize << 20
|
||||
}
|
||||
limitedReader := io.LimitReader(resp.Body, maxResponseSize)
|
||||
|
||||
// Copy the response body to the writer
|
||||
_, err = io.Copy(w, limitedReader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write fetched label data: %w", err)
|
||||
}
|
||||
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
log.Printf("failed to close response body: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func PrintLabel(cfg *config.Config, params *GenerateParameters) error {
|
||||
tmpFile := filepath.Join(os.TempDir(), fmt.Sprintf("label-%d.png", time.Now().UnixNano()))
|
||||
f, err := os.OpenFile(tmpFile, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
|
||||
@@ -393,7 +292,7 @@ func PrintLabel(cfg *config.Config, params *GenerateParameters) error {
|
||||
}
|
||||
}()
|
||||
|
||||
err = GenerateLabel(f, params, cfg)
|
||||
err = GenerateLabel(f, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -404,27 +303,8 @@ func PrintLabel(cfg *config.Config, params *GenerateParameters) error {
|
||||
|
||||
commandTemplate := template.Must(template.New("command").Parse(*cfg.LabelMaker.PrintCommand))
|
||||
builder := &strings.Builder{}
|
||||
additionalInformation := func() string {
|
||||
if params.AdditionalInformation != nil {
|
||||
return *params.AdditionalInformation
|
||||
}
|
||||
return ""
|
||||
}()
|
||||
if err := commandTemplate.Execute(builder, map[string]string{
|
||||
"FileName": f.Name(),
|
||||
"Width": fmt.Sprintf("%d", params.Width),
|
||||
"Height": fmt.Sprintf("%d", params.Height),
|
||||
"QrSize": fmt.Sprintf("%d", params.QrSize),
|
||||
"Margin": fmt.Sprintf("%d", params.Margin),
|
||||
"ComponentPadding": fmt.Sprintf("%d", params.ComponentPadding),
|
||||
"TitleText": params.TitleText,
|
||||
"TitleFontSize": fmt.Sprintf("%f", params.TitleFontSize),
|
||||
"DescriptionText": params.DescriptionText,
|
||||
"DescriptionFontSize": fmt.Sprintf("%f", params.DescriptionFontSize),
|
||||
"AdditionalInformation": additionalInformation,
|
||||
"Dpi": fmt.Sprintf("%f", params.Dpi),
|
||||
"URL": params.URL,
|
||||
"DynamicLength": fmt.Sprintf("%t", params.DynamicLength),
|
||||
"FileName": f.Name(),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
package textutils
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/text/runes"
|
||||
"golang.org/x/text/transform"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// RemoveAccents removes accents from text by normalizing Unicode characters
|
||||
// and removing diacritical marks. This allows for accent-insensitive search.
|
||||
//
|
||||
// Example:
|
||||
// - "electrónica" becomes "electronica"
|
||||
// - "café" becomes "cafe"
|
||||
// - "père" becomes "pere"
|
||||
func RemoveAccents(text string) string {
|
||||
// Create a transformer that:
|
||||
// 1. Normalizes to NFD (canonical decomposition)
|
||||
// 2. Removes diacritical marks (combining characters)
|
||||
// 3. Normalizes back to NFC (canonical composition)
|
||||
t := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
|
||||
|
||||
result, _, err := transform.String(t, text)
|
||||
if err != nil {
|
||||
// If transformation fails, return the original text
|
||||
return text
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// NormalizeSearchQuery normalizes a search query for accent-insensitive matching.
|
||||
// This function removes accents and converts to lowercase for consistent search behavior.
|
||||
func NormalizeSearchQuery(query string) string {
|
||||
normalized := RemoveAccents(query)
|
||||
return strings.ToLower(normalized)
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package textutils
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRemoveAccents(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "Spanish accented characters",
|
||||
input: "electrónica",
|
||||
expected: "electronica",
|
||||
},
|
||||
{
|
||||
name: "Spanish accented characters with tilde",
|
||||
input: "café",
|
||||
expected: "cafe",
|
||||
},
|
||||
{
|
||||
name: "French accented characters",
|
||||
input: "père",
|
||||
expected: "pere",
|
||||
},
|
||||
{
|
||||
name: "German umlauts",
|
||||
input: "Björk",
|
||||
expected: "Bjork",
|
||||
},
|
||||
{
|
||||
name: "Mixed accented characters",
|
||||
input: "résumé",
|
||||
expected: "resume",
|
||||
},
|
||||
{
|
||||
name: "Portuguese accented characters",
|
||||
input: "João",
|
||||
expected: "Joao",
|
||||
},
|
||||
{
|
||||
name: "No accents",
|
||||
input: "hello world",
|
||||
expected: "hello world",
|
||||
},
|
||||
{
|
||||
name: "Empty string",
|
||||
input: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "Numbers and symbols",
|
||||
input: "123!@#",
|
||||
expected: "123!@#",
|
||||
},
|
||||
{
|
||||
name: "Multiple accents in one word",
|
||||
input: "été",
|
||||
expected: "ete",
|
||||
},
|
||||
{
|
||||
name: "Complex Unicode characters",
|
||||
input: "français",
|
||||
expected: "francais",
|
||||
},
|
||||
{
|
||||
name: "Unicode diacritics",
|
||||
input: "naïve",
|
||||
expected: "naive",
|
||||
},
|
||||
{
|
||||
name: "Unicode combining characters",
|
||||
input: "e\u0301", // e with combining acute accent
|
||||
expected: "e",
|
||||
},
|
||||
{
|
||||
name: "Very long string with accents",
|
||||
input: strings.Repeat("café", 1000),
|
||||
expected: strings.Repeat("cafe", 1000),
|
||||
},
|
||||
{
|
||||
name: "All French accents",
|
||||
input: "àâäéèêëïîôöùûüÿç",
|
||||
expected: "aaaeeeeiioouuuyc",
|
||||
},
|
||||
{
|
||||
name: "All Spanish accents",
|
||||
input: "áéíóúñüÁÉÍÓÚÑÜ",
|
||||
expected: "aeiounuAEIOUNU",
|
||||
},
|
||||
{
|
||||
name: "All German umlauts",
|
||||
input: "äöüÄÖÜß",
|
||||
expected: "aouAOUß",
|
||||
},
|
||||
{
|
||||
name: "Mixed languages",
|
||||
input: "Français café España niño",
|
||||
expected: "Francais cafe Espana nino",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := RemoveAccents(tc.input)
|
||||
if result != tc.expected {
|
||||
t.Errorf("RemoveAccents(%q) = %q, expected %q", tc.input, result, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeSearchQuery(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "Uppercase with accents",
|
||||
input: "ELECTRÓNICA",
|
||||
expected: "electronica",
|
||||
},
|
||||
{
|
||||
name: "Mixed case with accents",
|
||||
input: "Electrónica",
|
||||
expected: "electronica",
|
||||
},
|
||||
{
|
||||
name: "Multiple words with accents",
|
||||
input: "Café París",
|
||||
expected: "cafe paris",
|
||||
},
|
||||
{
|
||||
name: "No accents mixed case",
|
||||
input: "Hello World",
|
||||
expected: "hello world",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := NormalizeSearchQuery(tc.input)
|
||||
if result != tc.expected {
|
||||
t.Errorf("NormalizeSearchQuery(%q) = %q, expected %q", tc.input, result, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ services:
|
||||
image: homebox
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./Dockerfile.hardened
|
||||
dockerfile: ./Dockerfile.rootless
|
||||
args:
|
||||
- COMMIT=head
|
||||
- BUILD_TIME=0001-01-01T00:00:00Z
|
||||
|
||||
@@ -941,48 +941,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/items/{id}/duplicate": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Items"
|
||||
],
|
||||
"summary": "Duplicate Item",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Item ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Duplicate Options",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/repo.DuplicateOptions"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/repo.ItemOut"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/items/{id}/maintenance": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -1851,41 +1809,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/products/search-from-barcode": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Items"
|
||||
],
|
||||
"summary": "Search EAN from Barcode",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "barcode to be searched",
|
||||
"name": "data",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.BarcodeProduct"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/qrcode": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -3138,54 +3061,6 @@
|
||||
"TypeTime"
|
||||
]
|
||||
},
|
||||
"repo.BarcodeProduct": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"barcode": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageBase64": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"item": {
|
||||
"$ref": "#/definitions/repo.ItemCreate"
|
||||
},
|
||||
"manufacturer": {
|
||||
"type": "string"
|
||||
},
|
||||
"modelNumber": {
|
||||
"description": "Identifications",
|
||||
"type": "string"
|
||||
},
|
||||
"notes": {
|
||||
"description": "Extras",
|
||||
"type": "string"
|
||||
},
|
||||
"search_engine_name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.DuplicateOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"copyAttachments": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyCustomFields": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyMaintenance": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyPrefix": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.Group": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -3696,7 +3571,7 @@
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"maxLength": 1000
|
||||
"maxLength": 255
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
|
||||
@@ -646,38 +646,6 @@ definitions:
|
||||
- TypeNumber
|
||||
- TypeBoolean
|
||||
- TypeTime
|
||||
repo.BarcodeProduct:
|
||||
properties:
|
||||
barcode:
|
||||
type: string
|
||||
imageBase64:
|
||||
type: string
|
||||
imageURL:
|
||||
type: string
|
||||
item:
|
||||
$ref: '#/definitions/repo.ItemCreate'
|
||||
manufacturer:
|
||||
type: string
|
||||
modelNumber:
|
||||
description: Identifications
|
||||
type: string
|
||||
notes:
|
||||
description: Extras
|
||||
type: string
|
||||
search_engine_name:
|
||||
type: string
|
||||
type: object
|
||||
repo.DuplicateOptions:
|
||||
properties:
|
||||
copyAttachments:
|
||||
type: boolean
|
||||
copyCustomFields:
|
||||
type: boolean
|
||||
copyMaintenance:
|
||||
type: boolean
|
||||
copyPrefix:
|
||||
type: string
|
||||
type: object
|
||||
repo.Group:
|
||||
properties:
|
||||
createdAt:
|
||||
@@ -1023,7 +991,7 @@ definitions:
|
||||
color:
|
||||
type: string
|
||||
description:
|
||||
maxLength: 1000
|
||||
maxLength: 255
|
||||
type: string
|
||||
name:
|
||||
maxLength: 255
|
||||
@@ -1979,32 +1947,6 @@ paths:
|
||||
summary: Update Item Attachment
|
||||
tags:
|
||||
- Items Attachments
|
||||
/v1/items/{id}/duplicate:
|
||||
post:
|
||||
parameters:
|
||||
- description: Item ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
- description: Duplicate Options
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/repo.DuplicateOptions'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: Created
|
||||
schema:
|
||||
$ref: '#/definitions/repo.ItemOut'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Duplicate Item
|
||||
tags:
|
||||
- Items
|
||||
/v1/items/{id}/maintenance:
|
||||
get:
|
||||
parameters:
|
||||
@@ -2601,27 +2543,6 @@ paths:
|
||||
summary: Test Notifier
|
||||
tags:
|
||||
- Notifiers
|
||||
/v1/products/search-from-barcode:
|
||||
get:
|
||||
parameters:
|
||||
- description: barcode to be searched
|
||||
in: query
|
||||
name: data
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/repo.BarcodeProduct'
|
||||
type: array
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Search EAN from Barcode
|
||||
tags:
|
||||
- Items
|
||||
/v1/qrcode:
|
||||
get:
|
||||
parameters:
|
||||
|
||||
@@ -11,7 +11,7 @@ aside: false
|
||||
|-----------------------------------------|----------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| HBOX_MODE | `production` | application mode used for runtime behavior can be one of: `development`, `production` |
|
||||
| HBOX_WEB_PORT | 7745 | port to run the web server on, if you're using docker do not change this |
|
||||
| HBOX_WEB_HOST | | host to run the web server on, if you're using docker do not change this. see below for examples |
|
||||
| HBOX_WEB_HOST | | host to run the web server on, if you're using docker do not change this |
|
||||
| HBOX_OPTIONS_ALLOW_REGISTRATION | true | allow users to register themselves |
|
||||
| HBOX_OPTIONS_AUTO_INCREMENT_ASSET_ID | true | auto-increments the asset_id field for new items |
|
||||
| HBOX_OPTIONS_CURRENCY_CONFIG | | json configuration file containing additional currencie |
|
||||
@@ -35,13 +35,10 @@ aside: false
|
||||
| HBOX_DATABASE_SQLITE_PATH | ./.data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1 | sets the directory path for Sqlite |
|
||||
| HBOX_DATABASE_HOST | | sets the hostname for a postgres database |
|
||||
| HBOX_DATABASE_PORT | | sets the port for a postgres database |
|
||||
| HBOX_DATABASE_USERNAME | | sets the username for a postgres connection (optional if using cert auth) |
|
||||
| HBOX_DATABASE_PASSWORD | | sets the password for a postgres connection (optional if using cert auth) |
|
||||
| HBOX_DATABASE_USERNAME | | sets the username for a postgres connection |
|
||||
| HBOX_DATABASE_PASSWORD | | sets the password for a postgres connection |
|
||||
| HBOX_DATABASE_DATABASE | | sets the database for a postgres connection |
|
||||
| HBOX_DATABASE_SSL_MODE | | sets the sslmode for a postgres connection |
|
||||
| HBOX_DATABASE_SSL_CERT | | sets the sslcert for a postgres connection (should be a path) |
|
||||
| HBOX_DATABASE_SSL_KEY | | sets the sslkey for a postgres connection (should be a path) |
|
||||
| HBOX_DATABASE_SSL_ROOTCERT | | sets the sslrootcert for a postgres connection (should be a path) |
|
||||
| HBOX_OPTIONS_CHECK_GITHUB_RELEASE | true | check for new github releases |
|
||||
| HBOX_LABEL_MAKER_WIDTH | 526 | width for generated labels in pixels |
|
||||
| HBOX_LABEL_MAKER_HEIGHT | 200 | height for generated labels in pixels |
|
||||
@@ -54,84 +51,6 @@ aside: false
|
||||
| HBOX_THUMBNAIL_WIDTH | 500 | width for generated thumbnails in pixels |
|
||||
| HBOX_THUMBNAIL_HEIGHT | 500 | height for generated thumbnails in pixels |
|
||||
|
||||
### HBOX_WEB_HOST examples
|
||||
|
||||
| Value | Notes |
|
||||
|-----------------------------|------------------------------------------------------------|
|
||||
| 0.0.0.0 | Visible all interfaces (default behaviour) |
|
||||
| 127.0.0.1 | Only visible on same host |
|
||||
| 100.64.0.1 | Only visible on a specific interface (e.g., VPN in a VPS). |
|
||||
| unix?path=/run/homebox.sock | Listen on unix socket at specified path |
|
||||
| sysd?name=homebox.socket | Listen on systemd socket |
|
||||
|
||||
For unix and systemd socket address syntax and available options, see the [anyhttp address-syntax documentation](https://pkg.go.dev/go.balki.me/anyhttp#readme-address-syntax).
|
||||
|
||||
#### Private network example
|
||||
|
||||
Below example starts homebox in an isolated network. The process cannot make
|
||||
any external requests (including check for newer release) and thus more secure.
|
||||
|
||||
```bash
|
||||
❯ sudo systemd-run --property=PrivateNetwork=yes --uid $UID --pty --same-dir --wait --collect homebox --web-host "unix?path=/run/user/$UID/homebox.sock"
|
||||
Running as unit: run-p74482-i74483.service
|
||||
Press ^] three times within 1s to disconnect TTY.
|
||||
2025/07/11 22:33:29 goose: no migrations to run. current version: 20250706190000
|
||||
10:33PM INF ../../../go/src/app/app/api/handlers/v1/v1_ctrl_auth.go:98 > registering auth provider name=local
|
||||
10:33PM INF ../../../go/src/app/app/api/main.go:275 > Server is running on unix?path=/run/user/1000/homebox.sock
|
||||
10:33PM ERR ../../../go/src/app/app/api/main.go:403 > failed to get latest github release error="failed to make latest version request: Get \"https://api.github.com/repos/sysadminsmedia/homebox/releases/l
|
||||
atest\": dial tcp: lookup api.github.com on [::1]:53: read udp [::1]:50951->[::1]:53: read: connection refused"
|
||||
10:33PM INF ../../../go/src/app/internal/web/mid/logger.go:36 > request received method=GET path=/ rid=hname/PoXyRgt6ol-000001
|
||||
10:33PM INF ../../../go/src/app/internal/web/mid/logger.go:41 > request finished method=GET path=/ rid=hname/PoXyRgt6ol-000001 status=0
|
||||
```
|
||||
|
||||
#### Systemd socket example
|
||||
|
||||
In the example below, Homebox listens on a systemd socket securely so that only
|
||||
the webserver (Caddy) can access it. Other processes/containers on the host
|
||||
cannot connect to Homebox directly, bypassing the webserver.
|
||||
|
||||
File: homebox.socket
|
||||
```systemd
|
||||
# /usr/local/lib/systemd/system/homebox.socket
|
||||
[Unit]
|
||||
Description=Homebox socket
|
||||
|
||||
[Socket]
|
||||
ListenStream=/run/homebox.sock
|
||||
SocketGroup=caddy
|
||||
SocketMode=0660
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
```
|
||||
|
||||
File: homebox.service
|
||||
```systemd
|
||||
# /usr/local/lib/systemd/system/homebox.service
|
||||
[Unit]
|
||||
Description=Homebox
|
||||
After=network.target
|
||||
Documentation=https://homebox.software
|
||||
|
||||
[Service]
|
||||
DynamicUser=yes
|
||||
StateDirectory=homebox
|
||||
Environment=HBOX_WEB_HOST=sysd?name=homebox.socket
|
||||
WorkingDirectory=/var/lib/homebox
|
||||
|
||||
ExecStart=/usr/local/bin/homebox
|
||||
|
||||
NoNewPrivileges=yes
|
||||
CapabilityBoundingSet=
|
||||
RestrictNamespaces=true
|
||||
SystemCallFilter=@system-service
|
||||
```
|
||||
Usage:
|
||||
|
||||
```bash
|
||||
systemctl start homebox.socket
|
||||
```
|
||||
|
||||
::: warning Security Considerations
|
||||
For postgreSQL in production:
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
/*
|
||||
X-Frame-Options: DENY
|
||||
X-Content-Type-Options: nosniff
|
||||
Content-Security-Policy: default-src 'self'; script-src 'report-sample' 'unsafe-inline' 'self' https://a.sysadmins.zone/js/embed.host.js https://static.cloudflareinsights.com/beacon.min.js/vcd15cbe7772f49c399c6a5babf22c1241717689176015 https://unpkg.com/@stoplight/elements/web-components.min.js; style-src 'report-sample' 'unsafe-inline' 'self' https://unpkg.com; object-src 'none'; base-uri 'self'; connect-src 'self' https://raw.githubusercontent.com; font-src 'self'; frame-src 'self' https://a.sysadmins.zone; img-src 'self' data: http://translate.sysadminsmedia.com; manifest-src 'self'; media-src 'self'; worker-src 'none';
|
||||
@@ -1,7 +0,0 @@
|
||||
name = "homebox-docs"
|
||||
compatibility_date = "2025-07-12"
|
||||
preview_urls = true
|
||||
|
||||
[assets]
|
||||
directory = ".vitepress/dist"
|
||||
not_found_handling = "single-page-application"
|
||||
@@ -2,10 +2,7 @@
|
||||
<Dialog v-if="isDesktop" :dialog-id="dialogId">
|
||||
<DialogScrollContent>
|
||||
<DialogHeader>
|
||||
<div class="mr-4 flex place-items-center justify-between">
|
||||
<DialogTitle>{{ title }}</DialogTitle>
|
||||
<slot name="header-actions" />
|
||||
</div>
|
||||
<DialogTitle>{{ title }}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<slot />
|
||||
@@ -32,9 +29,6 @@
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>{{ title }}</DrawerTitle>
|
||||
</DrawerHeader>
|
||||
<div class="flex justify-center">
|
||||
<slot name="header-actions" />
|
||||
</div>
|
||||
|
||||
<div class="m-2 overflow-y-auto p-2">
|
||||
<slot />
|
||||
@@ -45,14 +39,13 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useMediaQuery } from "@vueuse/core";
|
||||
import type { DialogID } from "@/components/ui/dialog-provider/utils";
|
||||
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from "@/components/ui/drawer";
|
||||
import { Dialog, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
|
||||
const isDesktop = useMediaQuery("(min-width: 768px)");
|
||||
|
||||
defineProps<{
|
||||
dialogId: DialogID;
|
||||
dialogId: string;
|
||||
title: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Dialog :dialog-id="DialogID.Import">
|
||||
<Dialog dialog-id="import">
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{{ $t("components.app.import_dialog.title") }}</DialogTitle>
|
||||
@@ -38,7 +38,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { DialogID } from "@/components/ui/dialog-provider/utils";
|
||||
import { toast } from "@/components/ui/sonner";
|
||||
import {
|
||||
Dialog,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { DialogID, type NoParamDialogIDs, type OptionalDialogIDs } from "@/components/ui/dialog-provider/utils";
|
||||
import {
|
||||
CommandDialog,
|
||||
CommandInput,
|
||||
@@ -15,7 +14,7 @@
|
||||
|
||||
export type QuickMenuAction =
|
||||
| { text: string; href: string; type: "navigate" }
|
||||
| { text: string; dialogId: NoParamDialogIDs | OptionalDialogIDs; shortcut: string; type: "create" };
|
||||
| { text: string; dialogId: string; shortcut: string; type: "create" };
|
||||
|
||||
const props = defineProps({
|
||||
actions: {
|
||||
@@ -28,11 +27,11 @@
|
||||
const { t } = useI18n();
|
||||
const { closeDialog, openDialog } = useDialog();
|
||||
|
||||
useDialogHotkey(DialogID.QuickMenu, { code: "Backquote", ctrl: true });
|
||||
useDialogHotkey("quick-menu", { code: "Backquote", ctrl: true });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommandDialog :dialog-id="DialogID.QuickMenu">
|
||||
<CommandDialog dialog-id="quick-menu">
|
||||
<CommandInput
|
||||
:placeholder="t('components.quick_menu.shortcut_hint')"
|
||||
@keydown="
|
||||
@@ -40,12 +39,12 @@
|
||||
const item = props.actions.filter(item => 'shortcut' in item).find(item => item.shortcut === e.key);
|
||||
if (item) {
|
||||
e.preventDefault();
|
||||
openDialog(item.dialogId as NoParamDialogIDs);
|
||||
openDialog(item.dialogId);
|
||||
}
|
||||
// if esc is pressed, close the dialog
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
closeDialog(DialogID.QuickMenu);
|
||||
closeDialog('quick-menu');
|
||||
}
|
||||
}
|
||||
"
|
||||
@@ -61,7 +60,7 @@
|
||||
@select="
|
||||
e => {
|
||||
e.preventDefault();
|
||||
openDialog(create.dialogId as NoParamDialogIDs);
|
||||
openDialog(create.dialogId);
|
||||
}
|
||||
"
|
||||
>
|
||||
@@ -77,7 +76,7 @@
|
||||
:value="`global.navigate_${i + 1}`"
|
||||
@select="
|
||||
() => {
|
||||
closeDialog(DialogID.QuickMenu);
|
||||
closeDialog('quick-menu');
|
||||
navigateTo(navigate.href);
|
||||
}
|
||||
"
|
||||
@@ -88,8 +87,8 @@
|
||||
value="scanner"
|
||||
@select="
|
||||
() => {
|
||||
closeDialog(DialogID.QuickMenu);
|
||||
openDialog(DialogID.Scanner);
|
||||
closeDialog('quick-menu');
|
||||
openDialog('scanner');
|
||||
}
|
||||
"
|
||||
>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Dialog :dialog-id="DialogID.Scanner">
|
||||
<Dialog dialog-id="scanner">
|
||||
<DialogScrollContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{{ t("scanner.title") }}</DialogTitle>
|
||||
@@ -13,25 +13,6 @@
|
||||
<MdiAlertCircleOutline class="text-destructive" />
|
||||
<span class="text-sm font-medium">{{ errorMessage }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="detectedBarcode"
|
||||
class="mb-5 flex flex-col items-center gap-2 rounded-md border border-accent-foreground bg-accent p-4 text-accent-foreground"
|
||||
role="alert"
|
||||
>
|
||||
<div class="flex">
|
||||
<MdiBarcode class="mr-2" />
|
||||
<span class="flex-1 text-center text-sm font-medium">
|
||||
{{ detectedBarcodeType }} {{ $t("scanner.barcode_detected_message") }}:
|
||||
<strong>{{ detectedBarcode }}</strong>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ButtonGroup>
|
||||
<Button :disabled="loading" type="submit" @click="handleButtonClick">
|
||||
{{ $t("scanner.barcode_fetch_data") }}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
<!-- eslint-disable-next-line tailwindcss/no-custom-classname -->
|
||||
<video ref="video" class="aspect-video w-full rounded-lg bg-muted shadow" poster="data:image/gif,AAAA"></video>
|
||||
<div class="mt-4">
|
||||
@@ -53,19 +34,16 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed } from "vue";
|
||||
import { BrowserMultiFormatReader, NotFoundException, BarcodeFormat } from "@zxing/library";
|
||||
import { BrowserMultiFormatReader, NotFoundException } from "@zxing/library";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { DialogID } from "@/components/ui/dialog-provider/utils";
|
||||
import { Dialog, DialogHeader, DialogTitle, DialogScrollContent } from "@/components/ui/dialog";
|
||||
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@/components/ui/select";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import MdiBarcode from "~icons/mdi/barcode";
|
||||
import MdiAlertCircleOutline from "~icons/mdi/alert-circle-outline";
|
||||
import { useDialog } from "@/components/ui/dialog-provider";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { activeDialog, openDialog, closeDialog } = useDialog();
|
||||
const open = computed(() => activeDialog && activeDialog.value === DialogID.Scanner);
|
||||
const { activeDialog } = useDialog();
|
||||
const open = computed(() => activeDialog.value === "scanner");
|
||||
|
||||
const sources = ref<MediaDeviceInfo[]>([]);
|
||||
const selectedSource = ref<string | null>(null);
|
||||
@@ -73,8 +51,6 @@
|
||||
const video = ref<HTMLVideoElement>();
|
||||
const codeReader = new BrowserMultiFormatReader();
|
||||
const errorMessage = ref<string | null>(null);
|
||||
const detectedBarcode = ref<string>("");
|
||||
const detectedBarcodeType = ref<string>("");
|
||||
|
||||
const handleError = (error: unknown) => {
|
||||
console.error("Scanner error:", error);
|
||||
@@ -92,10 +68,6 @@
|
||||
}
|
||||
};
|
||||
|
||||
const handleButtonClick = () => {
|
||||
openDialog(DialogID.ProductImport, { params: { barcode: detectedBarcode.value } });
|
||||
};
|
||||
|
||||
const startScanner = async () => {
|
||||
errorMessage.value = null;
|
||||
if (!(navigator && navigator.mediaDevices && "enumerateDevices" in navigator.mediaDevices)) {
|
||||
@@ -137,7 +109,6 @@
|
||||
|
||||
watch(open, async isOpen => {
|
||||
if (isOpen) {
|
||||
detectedBarcode.value = "";
|
||||
await startScanner();
|
||||
} else {
|
||||
stopScanner();
|
||||
@@ -158,27 +129,10 @@
|
||||
throw new Error(t("scanner.invalid_url"));
|
||||
}
|
||||
const sanitizedPath = url.pathname.replace(/[^a-zA-Z0-9-_/]/g, "");
|
||||
closeDialog(DialogID.Scanner);
|
||||
navigateTo(sanitizedPath);
|
||||
} catch (err) {
|
||||
// Check if it's a barcode for a new element
|
||||
const bcfmt = result.getBarcodeFormat();
|
||||
|
||||
switch (bcfmt) {
|
||||
case BarcodeFormat.EAN_13:
|
||||
case BarcodeFormat.UPC_A:
|
||||
case BarcodeFormat.UPC_E:
|
||||
case BarcodeFormat.UPC_EAN_EXTENSION:
|
||||
console.info("Barcode detected");
|
||||
detectedBarcode.value = result.getText();
|
||||
detectedBarcodeType.value = BarcodeFormat[bcfmt].replaceAll("_", "-");
|
||||
break;
|
||||
|
||||
default:
|
||||
handleError(err);
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
handleError(err);
|
||||
}
|
||||
}
|
||||
if (err && !(err instanceof NotFoundException)) {
|
||||
@@ -195,3 +149,9 @@
|
||||
stopScanner();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
video {
|
||||
object-fit: cover;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { themes } from "~~/lib/data/themes";
|
||||
import { useTheme } from "~/composables/use-theme";
|
||||
|
||||
const { setTheme } = useTheme();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="homebox grid grid-cols-1 gap-4 font-sans sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
|
||||
<div
|
||||
v-for="theme in themes"
|
||||
:key="theme.value"
|
||||
:class="'theme-' + theme.value"
|
||||
class="overflow-hidden rounded-lg border outline-2 outline-offset-2"
|
||||
:data-theme="theme.value"
|
||||
:data-set-theme="theme.value"
|
||||
data-act-class="outline"
|
||||
@click="setTheme(theme.value)"
|
||||
>
|
||||
<div :data-theme="theme.value" class="w-full cursor-pointer bg-background-accent text-foreground">
|
||||
<div class="grid grid-cols-5 grid-rows-3">
|
||||
<div class="col-start-1 row-start-1 bg-background"></div>
|
||||
<div class="col-start-1 row-start-2 bg-sidebar"></div>
|
||||
<div class="col-start-1 row-start-3 bg-background-accent"></div>
|
||||
<div class="col-span-4 col-start-2 row-span-3 row-start-1 flex flex-col gap-1 bg-background p-2">
|
||||
<div class="font-bold">{{ theme.label }}</div>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<div class="flex size-5 items-center justify-center rounded bg-primary lg:size-6">
|
||||
<div class="text-sm font-bold text-primary-foreground">A</div>
|
||||
</div>
|
||||
<div class="flex size-5 items-center justify-center rounded bg-secondary lg:size-6">
|
||||
<div class="text-sm font-bold text-secondary-foreground">A</div>
|
||||
</div>
|
||||
<div class="flex size-5 items-center justify-center rounded bg-accent lg:size-6">
|
||||
<div class="text-sm font-bold text-accent-foreground">A</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -15,7 +15,6 @@
|
||||
import "@vuepic/vue-datepicker/dist/main.css";
|
||||
import * as datelib from "~/lib/datelib/datelib";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { darkThemes } from "~/lib/data/themes";
|
||||
|
||||
const emit = defineEmits(["update:modelValue", "update:text"]);
|
||||
|
||||
@@ -35,7 +34,7 @@
|
||||
},
|
||||
});
|
||||
|
||||
const isDark = useIsThemeInList(darkThemes);
|
||||
const isDark = useIsDark();
|
||||
|
||||
const formatDate = (date: Date | string | number) => fmtDate(date, "human", "date");
|
||||
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
<template>
|
||||
<Dialog :dialog-id="DialogID.ProductImport">
|
||||
<DialogContent :class="'w-full md:max-w-xl lg:max-w-4xl'">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{{ $t("components.item.product_import.title") }}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div
|
||||
v-if="errorMessage"
|
||||
class="flex items-center gap-2 rounded-md border border-destructive bg-destructive/10 p-4 text-destructive"
|
||||
role="alert"
|
||||
>
|
||||
<MdiAlertCircleOutline class="text-destructive" />
|
||||
<span class="text-sm font-medium">{{ errorMessage }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<FormTextField
|
||||
v-model="barcode"
|
||||
:disabled="searching"
|
||||
class="w-[30%]"
|
||||
:label="$t('components.item.product_import.barcode')"
|
||||
@keyup.enter="retrieveProductInfo(barcode)"
|
||||
/>
|
||||
<Button
|
||||
:variant="searching ? 'destructive' : 'default'"
|
||||
class="mt-auto h-10"
|
||||
@click="retrieveProductInfo(barcode)"
|
||||
>
|
||||
<MdiLoading v-if="searching" class="animate-spin" />
|
||||
<div v-if="!searching" class="relative mx-2">
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<MdiBarcode class="size-5 group-hover:hidden" />
|
||||
</div>
|
||||
</div>
|
||||
{{ searching ? $t("global.cancel") : $t("components.item.product_import.search_item") }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<BaseCard>
|
||||
<Table class="w-full">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead
|
||||
v-for="h in headers"
|
||||
:key="h.value"
|
||||
class="text-no-transform bg-secondary text-sm text-secondary-foreground hover:bg-secondary/90"
|
||||
>
|
||||
<div
|
||||
class="flex items-center gap-1"
|
||||
:class="{
|
||||
'justify-center': h.align === 'center',
|
||||
}"
|
||||
>
|
||||
<template v-if="typeof h === 'string'">{{ h }}</template>
|
||||
<template v-else>{{ $t(h.text) }}</template>
|
||||
</div>
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
|
||||
<TableBody>
|
||||
<TableRow
|
||||
v-for="(p, index) in products"
|
||||
:key="index"
|
||||
class="cursor-pointer"
|
||||
:class="{ selected: selectedRow === index }"
|
||||
@click="selectProduct(index)"
|
||||
>
|
||||
<TableCell
|
||||
v-for="h in headers"
|
||||
:key="h.value"
|
||||
:class="{
|
||||
'text-center': h.align === 'center',
|
||||
}"
|
||||
>
|
||||
<template v-if="h.type === 'name'">
|
||||
<div class="flex items-center space-x-4">
|
||||
<img :src="p.imageBase64" class="w-16 rounded object-fill shadow-sm" alt="Product's photo" />
|
||||
<span class="text-sm font-medium">
|
||||
{{ p.item.name }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="h.type === 'url'">
|
||||
<NuxtLink class="underline" :to="'https://' + extractValue(p, h.value)" target="_blank">{{
|
||||
extractValue(p, h.value)
|
||||
}}</NuxtLink>
|
||||
</template>
|
||||
|
||||
<slot v-else :name="cell(h)">
|
||||
{{ extractValue(p, h.value) }}
|
||||
</slot>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</BaseCard>
|
||||
|
||||
<DialogFooter>
|
||||
<Button type="import" :disabled="selectedRow === -1" @click="createItem"> Import selected </Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { DialogID } from "@/components/ui/dialog-provider/utils";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import type { BarcodeProduct } from "~~/lib/api/types/data-contracts";
|
||||
import { useDialog } from "~/components/ui/dialog-provider";
|
||||
import MdiAlertCircleOutline from "~icons/mdi/alert-circle-outline";
|
||||
import MdiBarcode from "~icons/mdi/barcode";
|
||||
import MdiLoading from "~icons/mdi/loading";
|
||||
import type { TableData } from "~/components/Item/View/Table.types";
|
||||
|
||||
const { openDialog, registerOpenDialogCallback } = useDialog();
|
||||
const { t } = useI18n();
|
||||
|
||||
const searching = ref(false);
|
||||
const barcode = ref<string>("");
|
||||
const products = ref<BarcodeProduct[] | null>(null);
|
||||
const selectedRow = ref(-1);
|
||||
const errorMessage = ref<string | null>(null);
|
||||
|
||||
type BarcodeTableHeader = {
|
||||
text: string;
|
||||
value: string;
|
||||
align?: "left" | "center" | "right";
|
||||
type?: "name" | "url";
|
||||
};
|
||||
|
||||
const defaultHeaders = [
|
||||
{
|
||||
text: "items.name",
|
||||
value: "name",
|
||||
align: "center",
|
||||
type: "name",
|
||||
},
|
||||
{ text: "items.manufacturer", value: "manufacturer", align: "center" },
|
||||
{ text: "items.model_number", value: "modelNumber", align: "center" },
|
||||
{ text: "components.item.product_import.db_source", value: "search_engine_name", align: "center", type: "url" },
|
||||
] satisfies BarcodeTableHeader[];
|
||||
|
||||
// Need for later filtering
|
||||
const headers = defaultHeaders;
|
||||
|
||||
onMounted(() => {
|
||||
const cleanup = registerOpenDialogCallback(DialogID.ProductImport, params => {
|
||||
selectedRow.value = -1;
|
||||
searching.value = false;
|
||||
errorMessage.value = null;
|
||||
|
||||
if (params?.barcode) {
|
||||
// Reset if the barcode is different
|
||||
if (params.barcode !== barcode.value) {
|
||||
barcode.value = params.barcode;
|
||||
|
||||
retrieveProductInfo(barcode.value).then(() => {
|
||||
console.log("Processing finished");
|
||||
});
|
||||
}
|
||||
} else {
|
||||
barcode.value = "";
|
||||
products.value = null;
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(cleanup);
|
||||
});
|
||||
|
||||
const api = useUserApi();
|
||||
|
||||
function createItem() {
|
||||
if (
|
||||
products.value !== null &&
|
||||
products.value.length > 0 &&
|
||||
selectedRow.value >= 0 &&
|
||||
selectedRow.value < products.value.length
|
||||
) {
|
||||
const p = products.value![selectedRow.value];
|
||||
openDialog(DialogID.CreateItem, {
|
||||
params: { product: p },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function retrieveProductInfo(barcode: string) {
|
||||
errorMessage.value = null;
|
||||
|
||||
if (!barcode || barcode.trim().length === 0 || !/^[0-9]+$/.test(barcode)) {
|
||||
errorMessage.value = t("components.item.product_import.error_invalid_barcode");
|
||||
console.error(errorMessage.value);
|
||||
return;
|
||||
}
|
||||
|
||||
products.value = null;
|
||||
searching.value = true;
|
||||
|
||||
try {
|
||||
const result = await api.products.searchFromBarcode(barcode.trim());
|
||||
if (result.error) {
|
||||
errorMessage.value = t("errors.api_failure") + result.error;
|
||||
console.error(errorMessage.value);
|
||||
} else {
|
||||
if (result.data === undefined || result.data.length === undefined || result.data.length === 0) {
|
||||
errorMessage.value = t("components.item.product_import.error_not_found");
|
||||
}
|
||||
|
||||
products.value = result.data;
|
||||
}
|
||||
} catch (error) {
|
||||
errorMessage.value = t("components.item.product_import.error_exception") + error;
|
||||
console.error(errorMessage.value);
|
||||
} finally {
|
||||
searching.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function extractValue(data: TableData, value: string) {
|
||||
const parts = value.split(".");
|
||||
let current = data;
|
||||
for (const part of parts) {
|
||||
current = current[part];
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
function cell(h: BarcodeTableHeader) {
|
||||
return `cell-${h.value.replace(".", "_")}`;
|
||||
}
|
||||
|
||||
function selectProduct(index: number) {
|
||||
// Unselect if already selected
|
||||
if (selectedRow.value === index) {
|
||||
selectedRow.value = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
selectedRow.value = index;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
tr.selected {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--background));
|
||||
}
|
||||
|
||||
tr:hover.selected {
|
||||
background-color: hsl(var(--primary));
|
||||
}
|
||||
</style>
|
||||
@@ -1,34 +1,5 @@
|
||||
<template>
|
||||
<BaseModal :dialog-id="DialogID.CreateItem" :title="$t('components.item.create_modal.title')">
|
||||
<template #header-actions>
|
||||
<div class="flex">
|
||||
<TooltipProvider :delay-duration="0">
|
||||
<ButtonGroup>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button variant="outline" :disabled="loading" size="icon" data-pos="start" @click="openQrScannerPage()">
|
||||
<MdiBarcodeScan class="size-5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{{ $t("components.item.create_modal.product_tooltip_scan_barcode") }}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button variant="outline" :disabled="loading" size="icon" data-pos="end" @click="openBarcodeDialog()">
|
||||
<MdiBarcode class="size-5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{{ $t("components.item.create_modal.product_tooltip_input_barcode") }}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<BaseModal dialog-id="create-item" :title="$t('components.item.create_modal.title')">
|
||||
<form class="flex flex-col gap-2" @submit.prevent="create()">
|
||||
<LocationSelector v-model="form.location" />
|
||||
<ItemSelector
|
||||
@@ -169,7 +140,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { DialogID } from "@/components/ui/dialog-provider/utils";
|
||||
import { toast } from "@/components/ui/sonner";
|
||||
import { Button, ButtonGroup } from "~/components/ui/button";
|
||||
import BaseModal from "@/components/App/CreateModal.vue";
|
||||
@@ -178,8 +148,6 @@
|
||||
import type { ItemCreate, LocationOut } from "~~/lib/api/types/data-contracts";
|
||||
import { useLabelStore } from "~~/stores/labels";
|
||||
import { useLocationStore } from "~~/stores/locations";
|
||||
import MdiBarcode from "~icons/mdi/barcode";
|
||||
import MdiBarcodeScan from "~icons/mdi/barcode-scan";
|
||||
import MdiPackageVariant from "~icons/mdi/package-variant";
|
||||
import MdiPackageVariantClosed from "~icons/mdi/package-variant-closed";
|
||||
import MdiDelete from "~icons/mdi/delete";
|
||||
@@ -199,9 +167,9 @@
|
||||
}
|
||||
|
||||
const { t } = useI18n();
|
||||
const { openDialog, closeDialog, registerOpenDialogCallback } = useDialog();
|
||||
const { activeDialog, closeDialog } = useDialog();
|
||||
|
||||
useDialogHotkey(DialogID.CreateItem, { code: "Digit1", shift: true });
|
||||
useDialogHotkey("create-item", { code: "Digit1", shift: true });
|
||||
|
||||
const api = useUserApi();
|
||||
|
||||
@@ -299,69 +267,55 @@
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const cleanup = registerOpenDialogCallback(DialogID.CreateItem, async params => {
|
||||
// needed since URL will be cleared in the next step => ParentId Selection should stay though
|
||||
subItemCreate.value = subItemCreateParam.value === "y";
|
||||
let parentItemLocationId = null;
|
||||
watch(
|
||||
() => activeDialog.value,
|
||||
async active => {
|
||||
if (active === "create-item") {
|
||||
// needed since URL will be cleared in the next step => ParentId Selection should stay though
|
||||
subItemCreate.value = subItemCreateParam.value === "y";
|
||||
let parentItemLocationId = null;
|
||||
|
||||
if (subItemCreate.value && itemId.value) {
|
||||
const itemIdRead = typeof itemId.value === "string" ? (itemId.value as string) : itemId.value[0];
|
||||
const { data, error } = await api.items.get(itemIdRead);
|
||||
if (error || !data) {
|
||||
toast.error(t("components.item.create_modal.toast.failed_load_parent"));
|
||||
console.error("Parent item fetch error:", error);
|
||||
if (subItemCreate.value && itemId.value) {
|
||||
const itemIdRead = typeof itemId.value === "string" ? (itemId.value as string) : itemId.value[0];
|
||||
const { data, error } = await api.items.get(itemIdRead);
|
||||
if (error || !data) {
|
||||
toast.error(t("components.item.create_modal.toast.failed_load_parent"));
|
||||
console.error("Parent item fetch error:", error);
|
||||
}
|
||||
|
||||
if (data) {
|
||||
parent.value = data;
|
||||
}
|
||||
|
||||
if (data.location) {
|
||||
const { location } = data;
|
||||
parentItemLocationId = location.id;
|
||||
}
|
||||
|
||||
// clear URL Parameter (subItemCreate) since intention was communicated and received
|
||||
const currentQuery = { ...route.query };
|
||||
delete currentQuery.subItemCreate;
|
||||
await router.push({ query: currentQuery });
|
||||
} else {
|
||||
// since Input is hidden in this case, make sure no accidental parent information is sent out
|
||||
parent.value = {};
|
||||
form.parentId = null;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
parent.value = data;
|
||||
const locId = locationId.value ? locationId.value : parentItemLocationId;
|
||||
|
||||
if (locId) {
|
||||
const found = locations.value.find(l => l.id === locId);
|
||||
if (found) {
|
||||
form.location = found;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.location) {
|
||||
const { location } = data;
|
||||
parentItemLocationId = location.id;
|
||||
}
|
||||
|
||||
// clear URL Parameter (subItemCreate) since intention was communicated and received
|
||||
const currentQuery = { ...route.query };
|
||||
delete currentQuery.subItemCreate;
|
||||
await router.push({ query: currentQuery });
|
||||
} else {
|
||||
// since Input is hidden in this case, make sure no accidental parent information is sent out
|
||||
parent.value = {};
|
||||
form.parentId = null;
|
||||
}
|
||||
|
||||
const locId = locationId.value ? locationId.value : parentItemLocationId;
|
||||
|
||||
if (locId) {
|
||||
const found = locations.value.find(l => l.id === locId);
|
||||
if (found) {
|
||||
form.location = found;
|
||||
if (labelId.value) {
|
||||
form.labels = labels.value.filter(l => l.id === labelId.value).map(l => l.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (params?.product) {
|
||||
form.name = params.product.item.name;
|
||||
form.description = params.product.item.description;
|
||||
|
||||
if (params.product.imageURL) {
|
||||
form.photos.push({
|
||||
photoName: "product_view.jpg",
|
||||
fileBase64: params.product.imageBase64,
|
||||
primary: form.photos.length === 0,
|
||||
file: dataURLtoFile(params.product.imageBase64, "product_view.jpg"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (labelId.value) {
|
||||
form.labels = labels.value.filter(l => l.id === labelId.value).map(l => l.id);
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(cleanup);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
async function create(close = true) {
|
||||
if (!form.location?.id) {
|
||||
@@ -432,7 +386,7 @@
|
||||
loading.value = false;
|
||||
|
||||
if (close) {
|
||||
closeDialog(DialogID.CreateItem);
|
||||
closeDialog("create-item");
|
||||
navigateTo(`/item/${data.id}`);
|
||||
}
|
||||
}
|
||||
@@ -511,12 +465,4 @@
|
||||
offScreenCanvas.height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function openQrScannerPage() {
|
||||
openDialog(DialogID.Scanner);
|
||||
}
|
||||
|
||||
function openBarcodeDialog() {
|
||||
openDialog(DialogID.ProductImport);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import type { DuplicateSettings } from "~/composables/use-preferences";
|
||||
|
||||
type Props = {
|
||||
modelValue: DuplicateSettings;
|
||||
};
|
||||
|
||||
type Emits = {
|
||||
(e: "update:modelValue", value: DuplicateSettings): void;
|
||||
};
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const enableCustomPrefix = ref(props.modelValue.copyPrefixOverride !== null);
|
||||
const prefix = ref(props.modelValue.copyPrefixOverride ?? "");
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const settings = computed({
|
||||
get: () => props.modelValue,
|
||||
set: value => emit("update:modelValue", value),
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<Switch id="copy-maintenance" v-model="settings.copyMaintenance" />
|
||||
<Label for="copy-maintenance">
|
||||
{{ $t("items.duplicate.copy_maintenance") }}
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<Switch id="copy-attachments" v-model="settings.copyAttachments" />
|
||||
<Label for="copy-attachments">
|
||||
{{ $t("items.duplicate.copy_attachments") }}
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<Switch id="copy-custom-fields" v-model="settings.copyCustomFields" />
|
||||
<Label for="copy-custom-fields">
|
||||
{{ $t("items.duplicate.copy_custom_fields") }}
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<Switch
|
||||
id="copy-prefix"
|
||||
v-model="enableCustomPrefix"
|
||||
@update:model-value="
|
||||
v => {
|
||||
settings.copyPrefixOverride = v ? prefix : null;
|
||||
}
|
||||
"
|
||||
/>
|
||||
<Label for="copy-prefix">{{ $t("items.duplicate.enable_custom_prefix") }}</Label>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label for="copy-prefix" :class="{ 'opacity-50': !enableCustomPrefix }">
|
||||
{{ $t("items.duplicate.custom_prefix") }}
|
||||
</Label>
|
||||
<Input
|
||||
id="copy-prefix"
|
||||
v-model="prefix"
|
||||
:disabled="!enableCustomPrefix"
|
||||
:placeholder="$t('items.duplicate.prefix')"
|
||||
class="w-full"
|
||||
@input="settings.copyPrefixOverride = prefix"
|
||||
/>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{{ $t("items.duplicate.prefix_instructions") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,99 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
||||
import { buttonVariants, Button } from "@/components/ui/button";
|
||||
import { useDialog } from "@/components/ui/dialog-provider";
|
||||
import { DialogID } from "~/components/ui/dialog-provider/utils";
|
||||
import { useConfirm } from "@/composables/use-confirm";
|
||||
import { toast } from "@/components/ui/sonner";
|
||||
import MdiClose from "~icons/mdi/close";
|
||||
import MdiDownload from "~icons/mdi/download";
|
||||
import MdiDelete from "~icons/mdi/delete";
|
||||
|
||||
const { t } = useI18n();
|
||||
const confirm = useConfirm();
|
||||
|
||||
const { closeDialog, registerOpenDialogCallback } = useDialog();
|
||||
|
||||
const api = useUserApi();
|
||||
|
||||
const image = reactive<{
|
||||
attachmentId: string;
|
||||
itemId: string;
|
||||
originalSrc: string;
|
||||
originalType?: string;
|
||||
thumbnailSrc?: string;
|
||||
}>({
|
||||
attachmentId: "",
|
||||
itemId: "",
|
||||
originalSrc: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
const cleanup = registerOpenDialogCallback(DialogID.ItemImage, params => {
|
||||
image.attachmentId = params.attachmentId;
|
||||
image.itemId = params.itemId;
|
||||
if (params.type === "preloaded") {
|
||||
image.originalSrc = params.originalSrc;
|
||||
image.originalType = params.originalType;
|
||||
image.thumbnailSrc = params.thumbnailSrc;
|
||||
} else if (params.type === "attachment") {
|
||||
image.originalSrc = api.authURL(`/items/${params.itemId}/attachments/${params.attachmentId}`);
|
||||
image.originalType = params.mimeType;
|
||||
image.thumbnailSrc = params.thumbnailId
|
||||
? api.authURL(`/items/${params.itemId}/attachments/${params.thumbnailId}`)
|
||||
: image.originalSrc;
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(cleanup);
|
||||
});
|
||||
|
||||
async function deleteAttachment() {
|
||||
const confirmed = await confirm.open(t("items.delete_attachment_confirm"));
|
||||
|
||||
if (confirmed.isCanceled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { error } = await api.items.attachments.delete(image.itemId, image.attachmentId);
|
||||
|
||||
if (error) {
|
||||
toast.error(t("items.toast.failed_delete_attachment"));
|
||||
return;
|
||||
}
|
||||
|
||||
closeDialog(DialogID.ItemImage, {
|
||||
action: "delete",
|
||||
id: image.attachmentId,
|
||||
});
|
||||
toast.success(t("items.toast.attachment_deleted"));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog :dialog-id="DialogID.ItemImage">
|
||||
<DialogContent class="w-auto border-transparent bg-transparent p-0" disable-close>
|
||||
<picture>
|
||||
<source :srcset="image.originalSrc" :type="image.originalType" />
|
||||
<img :src="image.thumbnailSrc" alt="attachment image" />
|
||||
</picture>
|
||||
<Button variant="destructive" size="icon" class="absolute right-[84px] top-1" @click="deleteAttachment">
|
||||
<MdiDelete />
|
||||
</Button>
|
||||
<a :class="buttonVariants({ size: 'icon' })" :href="image.originalSrc" download class="absolute right-11 top-1">
|
||||
<MdiDownload />
|
||||
</a>
|
||||
<Button
|
||||
size="icon"
|
||||
class="absolute right-1 top-1"
|
||||
@click="
|
||||
closeDialog(DialogID.ItemImage);
|
||||
image.originalSrc = '';
|
||||
"
|
||||
>
|
||||
<MdiClose />
|
||||
</Button>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Dialog :dialog-id="DialogID.ItemTableSettings">
|
||||
<Dialog dialog-id="item-table-settings">
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{{ $t("components.item.view.table.table_settings") }}</DialogTitle>
|
||||
@@ -41,7 +41,7 @@
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button @click="closeDialog(DialogID.ItemTableSettings)"> {{ $t("global.save") }} </Button>
|
||||
<Button @click="closeDialog('item-table-settings')"> {{ $t("global.save") }} </Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
@@ -123,7 +123,7 @@
|
||||
hidden: disableControls,
|
||||
}"
|
||||
>
|
||||
<Button class="size-10 p-0" variant="outline" @click="openDialog(DialogID.ItemTableSettings)">
|
||||
<Button class="size-10 p-0" variant="outline" @click="openDialog('item-table-settings')">
|
||||
<MdiTableCog />
|
||||
</Button>
|
||||
<Pagination
|
||||
@@ -174,7 +174,6 @@
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { useDialog } from "@/components/ui/dialog-provider";
|
||||
import { DialogID } from "~/components/ui/dialog-provider/utils";
|
||||
|
||||
const { openDialog, closeDialog } = useDialog();
|
||||
|
||||
|
||||
@@ -42,6 +42,6 @@
|
||||
<MdiArrowUp class="hidden group-hover/label-chip:block" />
|
||||
</div>
|
||||
</div>
|
||||
{{ label.name }}
|
||||
{{ label.name.length > 20 ? `${label.name.substring(0, 20)}...` : label.name }}
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<BaseModal :dialog-id="DialogID.CreateLabel" :title="$t('components.label.create_modal.title')">
|
||||
<BaseModal dialog-id="create-label" :title="$t('components.label.create_modal.title')">
|
||||
<form class="flex flex-col gap-2" @submit.prevent="create()">
|
||||
<FormTextField
|
||||
v-model="form.name"
|
||||
@@ -12,7 +12,7 @@
|
||||
<FormTextArea
|
||||
v-model="form.description"
|
||||
:label="$t('components.label.create_modal.label_description')"
|
||||
:max-length="1000"
|
||||
:max-length="255"
|
||||
/>
|
||||
<ColorSelector v-model="form.color" :label="$t('components.label.create_modal.label_color')" :show-hex="true" />
|
||||
<div class="mt-4 flex flex-row-reverse">
|
||||
@@ -29,7 +29,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { DialogID } from "@/components/ui/dialog-provider/utils";
|
||||
import { toast } from "@/components/ui/sonner";
|
||||
import BaseModal from "@/components/App/CreateModal.vue";
|
||||
import { useDialog, useDialogHotkey } from "~/components/ui/dialog-provider";
|
||||
@@ -39,7 +38,7 @@
|
||||
|
||||
const { closeDialog } = useDialog();
|
||||
|
||||
useDialogHotkey(DialogID.CreateLabel, { code: "Digit2", shift: true });
|
||||
useDialogHotkey("create-label", { code: "Digit2", shift: true });
|
||||
|
||||
const loading = ref(false);
|
||||
const focused = ref(false);
|
||||
@@ -86,7 +85,7 @@
|
||||
reset();
|
||||
|
||||
if (close) {
|
||||
closeDialog(DialogID.CreateLabel);
|
||||
closeDialog("create-label");
|
||||
navigateTo(`/label/${data.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
<TagsInput
|
||||
v-model="modelValue"
|
||||
class="w-full gap-0 px-0"
|
||||
:display-value="v => props.labels.find(l => l.id === v)?.name ?? 'Loading...'"
|
||||
:display-value="v => shortenedLabels.find(l => l.id === v)?.name ?? 'Loading...'"
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-2 overflow-hidden px-3">
|
||||
<TagsInputItem v-for="item in modelValue" :key="item" :value="item" class="h-auto overflow-hidden text-wrap">
|
||||
<div class="flex flex-wrap items-center gap-2 px-3">
|
||||
<TagsInputItem v-for="item in modelValue" :key="item" :value="item">
|
||||
<span
|
||||
v-if="props.labels.find(l => l.id === item)?.color"
|
||||
class="ml-2 size-4 shrink-0 rounded-full"
|
||||
:style="{ backgroundColor: props.labels.find(l => l.id === item)?.color }"
|
||||
v-if="shortenedLabels.find(l => l.id === item)?.color"
|
||||
class="ml-2 inline-block size-4 rounded-full"
|
||||
:style="{ backgroundColor: shortenedLabels.find(l => l.id === item)?.color }"
|
||||
/>
|
||||
<TagsInputItemText class="py-0.5" />
|
||||
<TagsInputItemText />
|
||||
<TagsInputItemDelete />
|
||||
</TagsInputItem>
|
||||
</div>
|
||||
@@ -61,9 +61,9 @@
|
||||
"
|
||||
>
|
||||
<span
|
||||
class="mr-2 size-4 shrink-0 rounded-full align-middle"
|
||||
:class="{ border: props.labels.find(l => l.id === label.value)?.color }"
|
||||
:style="{ backgroundColor: props.labels.find(l => l.id === label.value)?.color }"
|
||||
class="mr-2 inline-block size-4 rounded-full align-middle"
|
||||
:class="{ border: shortenedLabels.find(l => l.id === label.value)?.color }"
|
||||
:style="{ backgroundColor: shortenedLabels.find(l => l.id === label.value)?.color }"
|
||||
/>
|
||||
{{ label.label }}
|
||||
</CommandItem>
|
||||
@@ -114,23 +114,24 @@
|
||||
const open = ref(false);
|
||||
const searchTerm = ref("");
|
||||
|
||||
const shortenedLabels = computed(() => {
|
||||
return props.labels.map(l => ({
|
||||
...l,
|
||||
name: l.name.length > 20 ? `${l.name.substring(0, 20)}...` : l.name,
|
||||
}));
|
||||
});
|
||||
|
||||
const filteredLabels = computed(() => {
|
||||
const filtered = fuzzysort
|
||||
.go(searchTerm.value, props.labels, { key: "name", all: true })
|
||||
.go(searchTerm.value, shortenedLabels.value, { key: "name", all: true })
|
||||
.map(l => ({
|
||||
value: l.obj.id,
|
||||
label: l.obj.name,
|
||||
}))
|
||||
.filter(i => !modelValue.value.includes(i.value));
|
||||
|
||||
// Only show "Create" option if search term is not empty and no exact match exists
|
||||
if (searchTerm.value.trim() !== "") {
|
||||
const trimmedSearchTerm = searchTerm.value.trim();
|
||||
const hasExactMatch = props.labels.some(label => label.name.toLowerCase() === trimmedSearchTerm.toLowerCase());
|
||||
|
||||
if (!hasExactMatch) {
|
||||
filtered.push({ value: "create-item", label: `${t("global.create")} ${searchTerm.value}` });
|
||||
}
|
||||
filtered.push({ value: "create-item", label: `${t("global.create")} ${searchTerm.value}` });
|
||||
}
|
||||
|
||||
return filtered;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<BaseModal :dialog-id="DialogID.CreateLocation" :title="$t('components.location.create_modal.title')">
|
||||
<BaseModal dialog-id="create-location" :title="$t('components.location.create_modal.title')">
|
||||
<form class="flex flex-col gap-2" @submit.prevent="create()">
|
||||
<LocationSelector v-model="form.parent" />
|
||||
<FormTextField
|
||||
@@ -31,7 +31,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { DialogID } from "@/components/ui/dialog-provider/utils";
|
||||
import { toast } from "@/components/ui/sonner";
|
||||
import { Button, ButtonGroup } from "~/components/ui/button";
|
||||
import BaseModal from "@/components/App/CreateModal.vue";
|
||||
@@ -42,7 +41,7 @@
|
||||
|
||||
const { activeDialog, closeDialog } = useDialog();
|
||||
|
||||
useDialogHotkey(DialogID.CreateLocation, { code: "Digit3", shift: true });
|
||||
useDialogHotkey("create-location", { code: "Digit3", shift: true });
|
||||
|
||||
const loading = ref(false);
|
||||
const focused = ref(false);
|
||||
@@ -55,11 +54,19 @@
|
||||
watch(
|
||||
() => activeDialog.value,
|
||||
active => {
|
||||
if (active && active === DialogID.CreateLocation) {
|
||||
if (active === "create-location") {
|
||||
// useTimeoutFn(() => {
|
||||
// focused.value = true;
|
||||
// }, 50);
|
||||
|
||||
if (locationId.value) {
|
||||
const found = locations.value.find(l => l.id === locationId.value);
|
||||
form.parent = found || null;
|
||||
if (found) {
|
||||
form.parent = found;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// focused.value = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -67,6 +74,7 @@
|
||||
function reset() {
|
||||
form.name = "";
|
||||
form.description = "";
|
||||
form.parent = null;
|
||||
focused.value = false;
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -110,11 +118,10 @@
|
||||
if (data) {
|
||||
toast.success(t("components.location.create_modal.toast.create_success"));
|
||||
}
|
||||
|
||||
reset();
|
||||
|
||||
if (close) {
|
||||
closeDialog(DialogID.CreateLocation);
|
||||
closeDialog("create-location");
|
||||
navigateTo(`/location/${data.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Dialog :dialog-id="DialogID.EditMaintenance">
|
||||
<Dialog dialog-id="edit-maintenance">
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
@@ -27,7 +27,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { DialogID } from "@/components/ui/dialog-provider/utils";
|
||||
import { toast } from "@/components/ui/sonner";
|
||||
import type { MaintenanceEntry, MaintenanceEntryWithDetails } from "~~/lib/api/types/data-contracts";
|
||||
import MdiPost from "~icons/mdi/post";
|
||||
@@ -78,7 +77,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
closeDialog(DialogID.EditMaintenance);
|
||||
closeDialog("edit-maintenance");
|
||||
emit("changed");
|
||||
}
|
||||
|
||||
@@ -100,7 +99,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
closeDialog(DialogID.EditMaintenance);
|
||||
closeDialog("edit-maintenance");
|
||||
emit("changed");
|
||||
}
|
||||
|
||||
@@ -112,7 +111,7 @@
|
||||
entry.description = "";
|
||||
entry.cost = "";
|
||||
entry.itemId = itemId;
|
||||
openDialog(DialogID.EditMaintenance);
|
||||
openDialog("edit-maintenance");
|
||||
};
|
||||
|
||||
const openUpdateModal = (maintenanceEntry: MaintenanceEntry | MaintenanceEntryWithDetails) => {
|
||||
@@ -123,7 +122,7 @@
|
||||
entry.description = maintenanceEntry.description;
|
||||
entry.cost = maintenanceEntry.cost;
|
||||
entry.itemId = null;
|
||||
openDialog(DialogID.EditMaintenance);
|
||||
openDialog("edit-maintenance");
|
||||
};
|
||||
|
||||
const confirm = useConfirm();
|
||||
@@ -165,7 +164,7 @@
|
||||
entry.description = maintenanceEntry.description;
|
||||
entry.cost = maintenanceEntry.cost;
|
||||
entry.itemId = itemId;
|
||||
openDialog(DialogID.EditMaintenance);
|
||||
openDialog("edit-maintenance");
|
||||
}
|
||||
|
||||
defineExpose({ openCreateModal, openUpdateModal, deleteEntry, complete, duplicate });
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { route } from "../../lib/api/base";
|
||||
import PageQRCode from "./PageQRCode.vue";
|
||||
import { DialogID } from "@/components/ui/dialog-provider/utils";
|
||||
import { toast } from "@/components/ui/sonner";
|
||||
import MdiLoading from "~icons/mdi/loading";
|
||||
import MdiPrinterPos from "~icons/mdi/printer-pos";
|
||||
@@ -64,7 +63,7 @@
|
||||
}
|
||||
|
||||
toast.success(t("components.global.label_maker.toast.print_success"));
|
||||
closeDialog(DialogID.PrintLabel);
|
||||
closeDialog("print-label");
|
||||
serverPrinting.value = false;
|
||||
}
|
||||
|
||||
@@ -94,7 +93,7 @@
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Dialog :dialog-id="DialogID.PrintLabel">
|
||||
<Dialog dialog-id="print-label">
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
@@ -138,7 +137,7 @@
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger as-child>
|
||||
<Button size="icon" @click="openDialog(DialogID.PrintLabel)">
|
||||
<Button size="icon" @click="openDialog('print-label')">
|
||||
<MdiPrinterPos name="mdi-printer-pos" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import MarkdownIt from "markdown-it";
|
||||
import { imgSize } from "@mdit/plugin-img-size";
|
||||
import DOMPurify from "dompurify";
|
||||
|
||||
type Props = {
|
||||
@@ -15,7 +14,7 @@
|
||||
html: true,
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
}).use(imgSize);
|
||||
});
|
||||
|
||||
const raw = computed(() => {
|
||||
const html = md.render(props.source || "").replace(/\n$/, ""); // remove trailing newline
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { DialogID } from "@/components/ui/dialog-provider/utils";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { route } from "~/lib/api/base";
|
||||
import MdiQrcode from "~icons/mdi/qrcode";
|
||||
@@ -17,7 +16,7 @@
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog :dialog-id="DialogID.PageQRCode">
|
||||
<Dialog dialog-id="page-qr-code">
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
@@ -30,7 +29,7 @@
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger as-child>
|
||||
<Button size="icon" @click="openDialog(DialogID.PageQRCode)">
|
||||
<Button size="icon" @click="openDialog('page-qr-code')">
|
||||
<MdiQrcode name="mdi-qrcode" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
|
||||
@@ -25,13 +25,13 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
<template>
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay
|
||||
class="fixed inset-0 z-[60] bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||
/>
|
||||
<AlertDialogContent
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'fixed left-1/2 top-1/2 z-[60] grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||
'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
|
||||
@@ -3,9 +3,8 @@ import type { DialogRootEmits, DialogRootProps } from 'reka-ui'
|
||||
import { Dialog, DialogContent } from '@/components/ui/dialog'
|
||||
import { useForwardPropsEmits } from 'reka-ui'
|
||||
import Command from './Command.vue'
|
||||
import type { DialogID } from '@/components/ui/dialog-provider/utils';
|
||||
|
||||
const props = defineProps<DialogRootProps & { dialogId: DialogID }>();
|
||||
const props = defineProps<DialogRootProps & { dialogId: string }>();
|
||||
const emits = defineEmits<DialogRootEmits>()
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits)
|
||||
|
||||
@@ -1,59 +1,25 @@
|
||||
<!-- DialogProvider.vue -->
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed } from 'vue';
|
||||
import {
|
||||
provideDialogContext,
|
||||
type DialogID,
|
||||
type DialogParamsMap,
|
||||
} from './utils';
|
||||
import { ref, reactive, computed } from "vue";
|
||||
import { provideDialogContext } from "./utils";
|
||||
|
||||
const activeDialog = ref<DialogID | null>(null);
|
||||
const activeDialog = ref<string | null>(null);
|
||||
const activeAlerts = reactive<string[]>([]);
|
||||
const openDialogCallbacks = new Map<DialogID, (params: any) => void>();
|
||||
|
||||
// onClose for the currently-open dialog (only one dialog can be active)
|
||||
let activeOnCloseCallback: ((result?: any) => void) | undefined;
|
||||
|
||||
const registerOpenDialogCallback = <T extends DialogID>(
|
||||
dialogId: T,
|
||||
callback: (params?: T extends keyof DialogParamsMap ? DialogParamsMap[T] : undefined) => void
|
||||
) => {
|
||||
openDialogCallbacks.set(dialogId, callback as (params: any) => void);
|
||||
return () => {
|
||||
openDialogCallbacks.delete(dialogId);
|
||||
};
|
||||
};
|
||||
|
||||
const openDialog = <T extends DialogID>(dialogId: T, options?: any) => {
|
||||
const openDialog = (dialogId: string) => {
|
||||
if (activeAlerts.length > 0) return;
|
||||
|
||||
activeDialog.value = dialogId;
|
||||
activeOnCloseCallback = options?.onClose;
|
||||
|
||||
const openCallback = openDialogCallbacks.get(dialogId);
|
||||
if (openCallback) {
|
||||
openCallback(options?.params);
|
||||
}
|
||||
};
|
||||
|
||||
function closeDialog(dialogId?: DialogID, result?: any) {
|
||||
// No dialogId passed -> close current active dialog without result
|
||||
if (!dialogId) {
|
||||
if (activeDialog.value) {
|
||||
// call onClose (if any) with no result
|
||||
activeOnCloseCallback?.(undefined);
|
||||
activeOnCloseCallback = undefined;
|
||||
const closeDialog = (dialogId?: string) => {
|
||||
if (dialogId) {
|
||||
if (activeDialog.value === dialogId) {
|
||||
activeDialog.value = null;
|
||||
}
|
||||
activeDialog.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// dialogId passed -> if it's the active dialog, call onClose with result
|
||||
if (activeDialog.value && activeDialog.value === dialogId) {
|
||||
activeOnCloseCallback?.(result);
|
||||
activeOnCloseCallback = undefined;
|
||||
} else {
|
||||
activeDialog.value = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addAlert = (alertId: string) => {
|
||||
activeAlerts.push(alertId);
|
||||
@@ -61,13 +27,14 @@
|
||||
|
||||
const removeAlert = (alertId: string) => {
|
||||
const index = activeAlerts.indexOf(alertId);
|
||||
if (index !== -1) activeAlerts.splice(index, 1);
|
||||
if (index !== -1) {
|
||||
activeAlerts.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Provide context to child components
|
||||
provideDialogContext({
|
||||
activeDialog: computed(() => activeDialog.value),
|
||||
registerOpenDialogCallback,
|
||||
openDialog,
|
||||
closeDialog,
|
||||
activeAlerts: computed(() => activeAlerts),
|
||||
|
||||
@@ -1,188 +1,56 @@
|
||||
import { computed, type ComputedRef } from 'vue';
|
||||
import { createContext } from 'reka-ui';
|
||||
import { useMagicKeys, useActiveElement } from '@vueuse/core';
|
||||
import type { BarcodeProduct } from '~~/lib/api/types/data-contracts';
|
||||
|
||||
export enum DialogID {
|
||||
AttachmentEdit = 'attachment-edit',
|
||||
ChangePassword = 'changePassword',
|
||||
CreateItem = 'create-item',
|
||||
CreateLocation = 'create-location',
|
||||
CreateLabel = 'create-label',
|
||||
CreateNotifier = 'create-notifier',
|
||||
DuplicateSettings = 'duplicate-settings',
|
||||
DuplicateTemporarySettings = 'duplicate-temporary-settings',
|
||||
EditMaintenance = 'edit-maintenance',
|
||||
Import = 'import',
|
||||
ItemImage = 'item-image',
|
||||
ItemTableSettings = 'item-table-settings',
|
||||
PrintLabel = 'print-label',
|
||||
ProductImport = 'product-import',
|
||||
QuickMenu = 'quick-menu',
|
||||
Scanner = 'scanner',
|
||||
PageQRCode = 'page-qr-code',
|
||||
UpdateLabel = 'update-label',
|
||||
UpdateLocation = 'update-location',
|
||||
}
|
||||
|
||||
/**
|
||||
* - Keys present without ? => params required
|
||||
* - Keys present with ? => params optional
|
||||
* - Keys not present => no params allowed
|
||||
*/
|
||||
export type DialogParamsMap = {
|
||||
[DialogID.ItemImage]:
|
||||
| ({
|
||||
type: 'preloaded';
|
||||
originalSrc: string;
|
||||
originalType?: string;
|
||||
thumbnailSrc?: string;
|
||||
}
|
||||
| {
|
||||
type: 'attachment';
|
||||
mimeType: string;
|
||||
thumbnailId?: string;
|
||||
}) & {
|
||||
itemId: string;
|
||||
attachmentId: string;
|
||||
};
|
||||
[DialogID.CreateItem]?: { product?: BarcodeProduct };
|
||||
[DialogID.ProductImport]?: { barcode?: string };
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines the payload type for a dialog's onClose callback.
|
||||
*/
|
||||
export type DialogResultMap = {
|
||||
[DialogID.ItemImage]?: { action: 'delete', id: string };
|
||||
};
|
||||
|
||||
/** Helpers to split IDs by requirement */
|
||||
type OptionalKeys<T> = {
|
||||
[K in keyof T]-?: {} extends Pick<T, K> ? K : never;
|
||||
}[keyof T];
|
||||
|
||||
type RequiredKeys<T> = Exclude<keyof T, OptionalKeys<T>>;
|
||||
|
||||
type SpecifiedDialogIDs = keyof DialogParamsMap;
|
||||
export type NoParamDialogIDs = Exclude<DialogID, SpecifiedDialogIDs>;
|
||||
export type RequiredDialogIDs = RequiredKeys<DialogParamsMap>;
|
||||
export type OptionalDialogIDs = OptionalKeys<DialogParamsMap>;
|
||||
|
||||
type ParamsOf<T extends DialogID> = T extends SpecifiedDialogIDs
|
||||
? DialogParamsMap[T]
|
||||
: never;
|
||||
|
||||
type ResultOf<T extends DialogID> = T extends keyof DialogResultMap
|
||||
? DialogResultMap[T]
|
||||
: void;
|
||||
|
||||
type OpenDialog = {
|
||||
// Dialogs with no parameters
|
||||
<T extends NoParamDialogIDs>(
|
||||
dialogId: T,
|
||||
options?: { onClose?: (result?: ResultOf<T>) => void; params?: never }
|
||||
): void;
|
||||
// Dialogs with required parameters
|
||||
<T extends RequiredDialogIDs>(
|
||||
dialogId: T,
|
||||
options: { params: ParamsOf<T>; onClose?: (result?: ResultOf<T>) => void }
|
||||
): void;
|
||||
// Dialogs with optional parameters
|
||||
<T extends OptionalDialogIDs>(
|
||||
dialogId: T,
|
||||
options?: { params?: ParamsOf<T>; onClose?: (result?: ResultOf<T>) => void }
|
||||
): void;
|
||||
};
|
||||
|
||||
type CloseDialog = {
|
||||
// Close the currently active dialog, no ID specified. No result payload.
|
||||
(): void;
|
||||
// Close a specific dialog that has a defined result type.
|
||||
<T extends keyof DialogResultMap>(dialogId: T, result?: ResultOf<T>): void;
|
||||
// Close a specific dialog that has NO defined result type.
|
||||
<T extends Exclude<DialogID, keyof DialogResultMap>>(
|
||||
dialogId: T,
|
||||
result?: never
|
||||
): void;
|
||||
};
|
||||
|
||||
type OpenCallback = {
|
||||
<T extends NoParamDialogIDs>(dialogId: T, cb: () => void): () => void;
|
||||
<T extends RequiredDialogIDs>(
|
||||
dialogId: T,
|
||||
cb: (params: ParamsOf<T>) => void
|
||||
): () => void;
|
||||
<T extends OptionalDialogIDs>(
|
||||
dialogId: T,
|
||||
cb: (params?: ParamsOf<T>) => void
|
||||
): () => void;
|
||||
};
|
||||
import type { ComputedRef } from "vue";
|
||||
import { createContext } from "reka-ui";
|
||||
import { useMagicKeys, useActiveElement } from "@vueuse/core";
|
||||
|
||||
export const [useDialog, provideDialogContext] = createContext<{
|
||||
activeDialog: ComputedRef<DialogID | null>;
|
||||
activeDialog: ComputedRef<string | null>;
|
||||
activeAlerts: ComputedRef<string[]>;
|
||||
registerOpenDialogCallback: OpenCallback;
|
||||
openDialog: OpenDialog;
|
||||
closeDialog: CloseDialog;
|
||||
openDialog: (dialogId: string) => void;
|
||||
closeDialog: (dialogId?: string) => void;
|
||||
addAlert: (alertId: string) => void;
|
||||
removeAlert: (alertId: string) => void;
|
||||
}>('DialogProvider');
|
||||
}>("DialogProvider");
|
||||
|
||||
/**
|
||||
* Hotkey helper:
|
||||
* - No/optional params: pass dialogId + key
|
||||
* - Required params: pass dialogId + key + getParams()
|
||||
*/
|
||||
type HotkeyKey = {
|
||||
shift?: boolean;
|
||||
ctrl?: boolean;
|
||||
code: string;
|
||||
};
|
||||
|
||||
export function useDialogHotkey<T extends NoParamDialogIDs | OptionalDialogIDs>(
|
||||
dialogId: T,
|
||||
key: HotkeyKey
|
||||
): void;
|
||||
export function useDialogHotkey<T extends RequiredDialogIDs>(
|
||||
dialogId: T,
|
||||
key: HotkeyKey,
|
||||
getParams: () => ParamsOf<T>
|
||||
): void;
|
||||
export function useDialogHotkey(
|
||||
dialogId: DialogID,
|
||||
key: HotkeyKey,
|
||||
getParams?: () => unknown
|
||||
) {
|
||||
export const useDialogHotkey = (
|
||||
dialogId: string,
|
||||
key: {
|
||||
shift?: boolean;
|
||||
ctrl?: boolean;
|
||||
code: string;
|
||||
}
|
||||
) => {
|
||||
const { openDialog } = useDialog();
|
||||
|
||||
const activeElement = useActiveElement();
|
||||
|
||||
const notUsingInput = computed(
|
||||
() =>
|
||||
activeElement.value?.tagName !== 'INPUT' &&
|
||||
activeElement.value?.tagName !== 'TEXTAREA'
|
||||
() => activeElement.value?.tagName !== "INPUT" && activeElement.value?.tagName !== "TEXTAREA"
|
||||
);
|
||||
|
||||
useMagicKeys({
|
||||
passive: false,
|
||||
onEventFired: (event) => {
|
||||
onEventFired: event => {
|
||||
// console.log({
|
||||
// event,
|
||||
// notUsingInput: notUsingInput.value,
|
||||
// eventType: event.type,
|
||||
// keyCode: event.code,
|
||||
// matchingKeyCode: key.code === event.code,
|
||||
// shift: event.shiftKey,
|
||||
// matchingShift: key.shift === undefined || event.shiftKey === key.shift,
|
||||
// ctrl: event.ctrlKey,
|
||||
// matchingCtrl: key.ctrl === undefined || event.ctrlKey === key.ctrl,
|
||||
// });
|
||||
if (
|
||||
notUsingInput.value &&
|
||||
event.type === 'keydown' &&
|
||||
event.type === "keydown" &&
|
||||
event.code === key.code &&
|
||||
(key.shift === undefined || event.shiftKey === key.shift) &&
|
||||
(key.ctrl === undefined || event.ctrlKey === key.ctrl)
|
||||
) {
|
||||
if (getParams) {
|
||||
openDialog(dialogId as RequiredDialogIDs, {
|
||||
params: getParams() as never,
|
||||
});
|
||||
} else {
|
||||
openDialog(dialogId as NoParamDialogIDs);
|
||||
}
|
||||
openDialog(dialogId);
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { DialogRoot, type DialogRootEmits, type DialogRootProps, useForwardPropsEmits } from "reka-ui";
|
||||
import { useDialog, type DialogID } from "@/components/ui/dialog-provider/utils";
|
||||
import { useDialog } from "../dialog-provider/utils";
|
||||
|
||||
const props = defineProps<DialogRootProps & { dialogId: DialogID }>();
|
||||
const props = defineProps<DialogRootProps & { dialogId: string }>();
|
||||
const emits = defineEmits<DialogRootEmits>();
|
||||
|
||||
const { closeDialog, activeDialog } = useDialog();
|
||||
|
||||
const isOpen = computed(() => (activeDialog.value && activeDialog.value === props.dialogId));
|
||||
const isOpen = computed(() => activeDialog.value === props.dialogId);
|
||||
const onOpenChange = (open: boolean) => {
|
||||
if (!open) closeDialog(props.dialogId as any);
|
||||
if (!open) closeDialog(props.dialogId);
|
||||
};
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits);
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
import type { DrawerRootEmits, DrawerRootProps } from "vaul-vue";
|
||||
import { useForwardPropsEmits } from "reka-ui";
|
||||
import { DrawerRoot } from "vaul-vue";
|
||||
import { DialogID, useDialog } from "@/components/ui/dialog-provider/utils";
|
||||
import { useDialog } from "../dialog-provider/utils";
|
||||
|
||||
const props = withDefaults(defineProps<DrawerRootProps & { dialogId: string }>(), {
|
||||
shouldScaleBackground: true,
|
||||
}) as DrawerRootProps & { dialogId: DialogID };
|
||||
}) as DrawerRootProps & { dialogId: string };
|
||||
|
||||
const emits = defineEmits<DrawerRootEmits>();
|
||||
|
||||
const { closeDialog, activeDialog } = useDialog();
|
||||
|
||||
const isOpen = computed(() => activeDialog.value !== null && activeDialog.value === props.dialogId);
|
||||
const isOpen = computed(() => activeDialog.value === props.dialogId);
|
||||
const onOpenChange = (open: boolean) => {
|
||||
if (!open) closeDialog(props.dialogId as any);
|
||||
if (!open) closeDialog(props.dialogId);
|
||||
};
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits);
|
||||
|
||||
@@ -4,13 +4,6 @@ import type { DaisyTheme } from "~~/lib/data/themes";
|
||||
|
||||
export type ViewType = "table" | "card" | "tree";
|
||||
|
||||
export type DuplicateSettings = {
|
||||
copyMaintenance: boolean;
|
||||
copyAttachments: boolean;
|
||||
copyCustomFields: boolean;
|
||||
copyPrefixOverride: string | null;
|
||||
};
|
||||
|
||||
export type LocationViewPreferences = {
|
||||
showDetails: boolean;
|
||||
showEmpty: boolean;
|
||||
@@ -22,7 +15,6 @@ export type LocationViewPreferences = {
|
||||
displayLegacyHeader: boolean;
|
||||
language?: string;
|
||||
overrideFormatLocale?: string;
|
||||
duplicateSettings: DuplicateSettings;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -42,12 +34,6 @@ export function useViewPreferences(): Ref<LocationViewPreferences> {
|
||||
displayLegacyHeader: false,
|
||||
language: null,
|
||||
overrideFormatLocale: null,
|
||||
duplicateSettings: {
|
||||
copyMaintenance: false,
|
||||
copyAttachments: true,
|
||||
copyCustomFields: true,
|
||||
copyPrefixOverride: null,
|
||||
},
|
||||
},
|
||||
{ mergeDefaults: true }
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ComputedRef } from "vue";
|
||||
import { type DaisyTheme } from "~~/lib/data/themes";
|
||||
import type { DaisyTheme } from "~~/lib/data/themes";
|
||||
|
||||
export interface UseTheme {
|
||||
theme: ComputedRef<DaisyTheme>;
|
||||
@@ -42,11 +42,27 @@ export function useTheme(): UseTheme {
|
||||
return { theme, setTheme };
|
||||
}
|
||||
|
||||
export function useIsThemeInList(list: DaisyTheme[]) {
|
||||
export function useIsDark() {
|
||||
const theme = useTheme();
|
||||
|
||||
const darkthemes = [
|
||||
"synthwave",
|
||||
"retro",
|
||||
"cyberpunk",
|
||||
"valentine",
|
||||
"halloween",
|
||||
"forest",
|
||||
"aqua",
|
||||
"black",
|
||||
"luxury",
|
||||
"dracula",
|
||||
"business",
|
||||
"night",
|
||||
"coffee",
|
||||
];
|
||||
|
||||
return computed(() => {
|
||||
return list.includes(theme.theme.value);
|
||||
return darkthemes.includes(theme.theme.value);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
<ItemCreateModal />
|
||||
<LabelCreateModal />
|
||||
<LocationCreateModal />
|
||||
<ItemBarcodeModal />
|
||||
<AppQuickMenuModal :actions="quickMenuActions" />
|
||||
<AppScannerModal />
|
||||
<SidebarProvider :default-open="sidebarState">
|
||||
@@ -42,7 +41,7 @@
|
||||
v-for="btn in dropdown"
|
||||
:key="btn.id"
|
||||
class="group cursor-pointer text-lg"
|
||||
@click="openDialog(btn.dialogId as NoParamDialogIDs)"
|
||||
@click="openDialog(btn.dialogId)"
|
||||
>
|
||||
{{ btn.name.value }}
|
||||
<Shortcut
|
||||
@@ -79,7 +78,7 @@
|
||||
'text-nowrap': typeof locale === 'string' && locale.startsWith('zh-'),
|
||||
}"
|
||||
:tooltip="$t('menu.scanner')"
|
||||
@click.prevent="openDialog(DialogID.Scanner)"
|
||||
@click.prevent="openDialog('scanner')"
|
||||
>
|
||||
<MdiQrcodeScan />
|
||||
<span>{{ $t("menu.scanner") }}</span>
|
||||
@@ -210,7 +209,6 @@
|
||||
import { Input } from "~/components/ui/input";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { toast } from "@/components/ui/sonner";
|
||||
import { DialogID, type NoParamDialogIDs, type OptionalDialogIDs } from "~/components/ui/dialog-provider/utils";
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const username = computed(() => authCtx.user?.name || "User");
|
||||
@@ -251,7 +249,7 @@
|
||||
navigator.mediaDevices
|
||||
.getUserMedia({ video: true })
|
||||
.then(() => {
|
||||
openDialog(DialogID.Scanner);
|
||||
openDialog("scanner");
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
@@ -265,31 +263,24 @@
|
||||
// Preload currency format
|
||||
useFormatCurrency();
|
||||
|
||||
type DropdownItem = {
|
||||
id: number;
|
||||
name: ComputedRef<string>;
|
||||
shortcut: string;
|
||||
dialogId: NoParamDialogIDs | OptionalDialogIDs;
|
||||
};
|
||||
|
||||
const dropdown: DropdownItem[] = [
|
||||
const dropdown = [
|
||||
{
|
||||
id: 0,
|
||||
name: computed(() => t("menu.create_item")),
|
||||
shortcut: "Shift+1",
|
||||
dialogId: DialogID.CreateItem,
|
||||
dialogId: "create-item",
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: computed(() => t("menu.create_location")),
|
||||
shortcut: "Shift+3",
|
||||
dialogId: DialogID.CreateLocation,
|
||||
dialogId: "create-location",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: computed(() => t("menu.create_label")),
|
||||
shortcut: "Shift+2",
|
||||
dialogId: DialogID.CreateLabel,
|
||||
dialogId: "create-label",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -343,7 +334,7 @@
|
||||
const quickMenuActions = reactive([
|
||||
...dropdown.map(v => ({
|
||||
text: computed(() => v.name.value),
|
||||
dialogId: v.dialogId as NoParamDialogIDs,
|
||||
dialogId: v.dialogId,
|
||||
shortcut: v.shortcut.split("+")[1],
|
||||
type: "create" as const,
|
||||
})),
|
||||
|
||||
@@ -153,26 +153,6 @@ export class ItemsApi extends BaseAPI {
|
||||
return resp;
|
||||
}
|
||||
|
||||
duplicate(
|
||||
id: string,
|
||||
options: {
|
||||
copyMaintenance?: boolean;
|
||||
copyAttachments?: boolean;
|
||||
copyCustomFields?: boolean;
|
||||
copyPrefix?: string;
|
||||
} = {}
|
||||
) {
|
||||
return this.http.post<typeof options, ItemOut>({
|
||||
url: route(`/items/${id}/duplicate`),
|
||||
body: {
|
||||
copyMaintenance: options.copyMaintenance,
|
||||
copyAttachments: options.copyAttachments,
|
||||
copyCustomFields: options.copyCustomFields,
|
||||
copyPrefix: options.copyPrefix,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
import(file: File | Blob) {
|
||||
const formData = new FormData();
|
||||
formData.append("csv", file);
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import { BaseAPI, route } from "../base";
|
||||
import type { BarcodeProduct } from "../types/data-contracts";
|
||||
|
||||
export class ProductAPI extends BaseAPI {
|
||||
searchFromBarcode(productEAN: string) {
|
||||
return this.http.get<BarcodeProduct[]>({ url: route(`/products/search-from-barcode`, { productEAN }) });
|
||||
}
|
||||
}
|
||||
@@ -451,19 +451,6 @@ export interface EntUserEdges {
|
||||
notifiers: EntNotifier[];
|
||||
}
|
||||
|
||||
export interface BarcodeProduct {
|
||||
barcode: string;
|
||||
imageBase64: string;
|
||||
imageURL: string;
|
||||
item: ItemCreate;
|
||||
manufacturer: string;
|
||||
/** Identifications */
|
||||
modelNumber: string;
|
||||
/** Extras */
|
||||
notes: string;
|
||||
search_engine_name: string;
|
||||
}
|
||||
|
||||
export interface Group {
|
||||
createdAt: Date | string;
|
||||
currency: string;
|
||||
@@ -644,7 +631,7 @@ export interface ItemUpdate {
|
||||
|
||||
export interface LabelCreate {
|
||||
color: string;
|
||||
/** @maxLength 1000 */
|
||||
/** @maxLength 255 */
|
||||
description: string;
|
||||
/**
|
||||
* @minLength 1
|
||||
|
||||
@@ -10,7 +10,6 @@ import { AssetsApi } from "./classes/assets";
|
||||
import { ReportsAPI } from "./classes/reports";
|
||||
import { NotifiersAPI } from "./classes/notifiers";
|
||||
import { MaintenanceAPI } from "./classes/maintenance";
|
||||
import { ProductAPI } from "./classes/product";
|
||||
import type { Requests } from "~~/lib/requests";
|
||||
|
||||
export class UserClient extends BaseAPI {
|
||||
@@ -25,7 +24,6 @@ export class UserClient extends BaseAPI {
|
||||
assets: AssetsApi;
|
||||
reports: ReportsAPI;
|
||||
notifiers: NotifiersAPI;
|
||||
products: ProductAPI;
|
||||
|
||||
constructor(requests: Requests, attachmentToken: string) {
|
||||
super(requests, attachmentToken);
|
||||
@@ -41,7 +39,6 @@ export class UserClient extends BaseAPI {
|
||||
this.assets = new AssetsApi(requests);
|
||||
this.reports = new ReportsAPI(requests);
|
||||
this.notifiers = new NotifiersAPI(requests);
|
||||
this.products = new ProductAPI(requests);
|
||||
|
||||
Object.freeze(this);
|
||||
}
|
||||
|
||||
@@ -153,19 +153,3 @@ export const themes: ThemeOption[] = [
|
||||
value: "winter",
|
||||
},
|
||||
];
|
||||
|
||||
export const darkThemes: DaisyTheme[] = [
|
||||
"synthwave",
|
||||
"retro",
|
||||
"cyberpunk",
|
||||
"valentine",
|
||||
"halloween",
|
||||
"forest",
|
||||
"aqua",
|
||||
"black",
|
||||
"luxury",
|
||||
"dracula",
|
||||
"business",
|
||||
"night",
|
||||
"coffee",
|
||||
];
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
"parent_location": "Ubicació pare"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "No hi ha ubicacions disponibles. Afegiu ubicacions amb el botó\n '<span class=\"link-primary\">'Crea'</span>' a la barra de navegació."
|
||||
"no_locations": "No hi ha ubicacions disponibles. Afegiu ubicacions amb el botó\n `<`span class=\"link-primary\"`>`Crea`<`/span`>` a la barra de navegació."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -355,6 +355,6 @@
|
||||
"bill_of_materials": "Llista de materials",
|
||||
"bill_of_materials_button": "Genera llista de materials"
|
||||
},
|
||||
"reports_sub": "Genera informes per a l'inventari."
|
||||
"reports_sub": "Genera informes per a l'inventari"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,8 +100,6 @@
|
||||
"item_photo": "Fotografie položky 📷",
|
||||
"item_quantity": "Množství položek",
|
||||
"parent_item": "Nadřazená položka",
|
||||
"product_tooltip_input_barcode": "Automatické vyplnění ručně zadaným čárovým kódem",
|
||||
"product_tooltip_scan_barcode": "Automatické vyplnění čárovým kódem z 📷",
|
||||
"rotate_photo": "Otočit fotku",
|
||||
"set_as_primary_photo": "Nastavit jako { isPrimary, select, true {non-} false {} other {}}primární fotku",
|
||||
"title": "Vytvořit položku",
|
||||
@@ -122,15 +120,6 @@
|
||||
"upload_photos": "Nahrát fotografie",
|
||||
"uploaded": "Fotka byla nahrána"
|
||||
},
|
||||
"product_import": {
|
||||
"barcode": "Čárový kód produktu",
|
||||
"db_source": "Zdroj DB",
|
||||
"error_exception": "Při načítání čárového kódu položky došlo k výjimce: ",
|
||||
"error_invalid_barcode": "Byl zadán neplatný čárový kód",
|
||||
"error_not_found": "Žádný produkt s daným čárovým kódem nebyl nalezen.",
|
||||
"search_item": "Vyhledat produkt",
|
||||
"title": "Importovat produkt"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Nebyly nalezeny žádné výsledky",
|
||||
"placeholder": "Vyberte…",
|
||||
@@ -195,9 +184,6 @@
|
||||
"shortcut_hint": "Pomocí číselných tlačítek rychle vyberte akci."
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"api_failure": "Volání backendového API selhalo: "
|
||||
},
|
||||
"global": {
|
||||
"add": "Přidat",
|
||||
"archived": "Archivované",
|
||||
@@ -573,8 +559,6 @@
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
"barcode_detected_message": "byl zjištěn čárový kód produktu",
|
||||
"barcode_fetch_data": "Načíst produktová data",
|
||||
"error": "Při skenování došlo k chybě",
|
||||
"invalid_url": "Neplatná adresa URL čárového kódu",
|
||||
"no_sources": "Nejsou k dispozici žádné zdroje videa",
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
{
|
||||
"components": {
|
||||
"app": {
|
||||
"create_modal": {
|
||||
"createAndAddAnother": "Brug {shiftKey} + {enterKey} til at oprette og tilføje en ny.",
|
||||
"enter": "Indtast",
|
||||
"shift": "Flytte"
|
||||
},
|
||||
"import_dialog": {
|
||||
"change_warning": "Adfærd for imports med eksisterende import_refs har ændret sig. Hvis en import_ref er tilstede i CSV filen,\nvil genstanden blive opdateret med værdierne fra CSV filen.",
|
||||
"description": "Importer en CSV fil som indeholder dine genstande, etiketter, og lokationer. Se dokumentation for mere information vedrørende\nden korrekte format.",
|
||||
"title": "Importer CSV Fil",
|
||||
"toast": {
|
||||
"import_failed": "Importen mislykkedes. Prøv igen senere.",
|
||||
"import_success": "Importen er gennemført!",
|
||||
"please_select_file": "Vælg venligst en fil, der skal importeres."
|
||||
}
|
||||
"title": "Importer CSV Fil"
|
||||
},
|
||||
"outdated": {
|
||||
"current_version": "Nuværende version",
|
||||
@@ -24,18 +14,6 @@
|
||||
"new_version_available_link": "Klik her for at læse udgivelsesnoterne"
|
||||
}
|
||||
},
|
||||
"color_selector": {
|
||||
"clear": "Reset farve",
|
||||
"color": "Farve",
|
||||
"no_color": "Ingen farve",
|
||||
"no_color_selected": "Ingen farve valgt",
|
||||
"randomize": "Tilfældig farve"
|
||||
},
|
||||
"form": {
|
||||
"password": {
|
||||
"toggle_show": "Slå adgangskode til/fra Vis"
|
||||
}
|
||||
},
|
||||
"global": {
|
||||
"copy_text": {
|
||||
"documentation": "Dokumentation",
|
||||
@@ -73,12 +51,7 @@
|
||||
"download": "Hent label",
|
||||
"print": "Print label",
|
||||
"server_print": "Print på Server",
|
||||
"titles": "Labels",
|
||||
"toast": {
|
||||
"load_status_failed": "Status kunne ikke indlæses",
|
||||
"print_failed": "Kunne ikke udskrive etiketten",
|
||||
"print_success": "Etiket udskrevet"
|
||||
}
|
||||
"titles": "Labels"
|
||||
},
|
||||
"page_qr_code": {
|
||||
"page_url": "Side URL",
|
||||
@@ -89,50 +62,12 @@
|
||||
}
|
||||
},
|
||||
"item": {
|
||||
"attachments_list": {
|
||||
"download": "Download",
|
||||
"open_new_tab": "Åbn i ny fane"
|
||||
},
|
||||
"create_modal": {
|
||||
"delete_photo": "Slet billede",
|
||||
"item_description": "Genstandsbeskrivelse",
|
||||
"item_name": "Genstandsnavn",
|
||||
"item_photo": "Vare Foto 📷",
|
||||
"item_quantity": "Vare Antal",
|
||||
"parent_item": "Overordnet element",
|
||||
"product_tooltip_scan_barcode": "Fyld automatisk med stregkode fra 📷",
|
||||
"rotate_photo": "Roter foto",
|
||||
"set_as_primary_photo": "Sæt som { isPrimary, select, true {non-} false {} other {}}primært foto",
|
||||
"title": "Opret genstand",
|
||||
"toast": {
|
||||
"already_creating": "Opretter allerede et element",
|
||||
"create_failed": "Kunne ikke oprette elementet",
|
||||
"create_success": "Element oprettet",
|
||||
"failed_load_parent": "Kunne ikke indlæse overordnet element - vælg venligst manuelt",
|
||||
"no_canvas_support": "Din browser understøtter ikke canvas-handlinger",
|
||||
"please_select_location": "Vælg venligst en placering.",
|
||||
"rotate_failed": "Kunne ikke rotere billedet: { error }",
|
||||
"rotate_process_failed": "Kunne ikke behandle roteret billede",
|
||||
"some_photos_failed": "{count, plural, =0 {Ingen billeder at uploade.} =1 {1 billede kunne ikke uploades.} other {Nogle billeder kunne ikke uploades.}}",
|
||||
"upload_failed": "Kunne ikke uploade billede: { photoName }",
|
||||
"upload_success": "{count, plural, =0 {Ingen billeder uploadet.} =1 {Foto uploadet.} other {Alle billeder uploadet.}}",
|
||||
"uploading_photos": "{count, plural, =0 {Ingen billeder at uploade} =1 {Uploader 1 billede…} other {Uploader {count} billeder…}}"
|
||||
},
|
||||
"upload_photos": "Upload Billeder",
|
||||
"uploaded": "Uploadet billede"
|
||||
},
|
||||
"product_import": {
|
||||
"barcode": "Produkts stregkode",
|
||||
"db_source": "DB kilde",
|
||||
"error_invalid_barcode": "Ugyldig stregkode angivet",
|
||||
"error_not_found": "Intet produkt fundet med angivet stregkode.",
|
||||
"search_item": "Søg produkt",
|
||||
"title": "Importér produkt"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Ingen resultater fundet",
|
||||
"placeholder": "Vælg…",
|
||||
"search_placeholder": "Skriv for at søge…"
|
||||
"upload_photos": "Upload Billeder"
|
||||
},
|
||||
"view": {
|
||||
"selectable": {
|
||||
@@ -142,26 +77,17 @@
|
||||
"table": "Tabel"
|
||||
},
|
||||
"table": {
|
||||
"headers": "Overskrifter",
|
||||
"page": "Side",
|
||||
"rows_per_page": "Rækker per side",
|
||||
"table_settings": "Tabel Indstillinger",
|
||||
"view_item": "Se vare"
|
||||
"table_settings": "Tabel Indstillinger"
|
||||
}
|
||||
}
|
||||
},
|
||||
"label": {
|
||||
"create_modal": {
|
||||
"label_color": "Etiketfarve",
|
||||
"label_description": "Etiketbeskrivelse",
|
||||
"label_name": "Etiketnavn",
|
||||
"title": "Opret label",
|
||||
"toast": {
|
||||
"already_creating": "Allerede oprettet en etiket",
|
||||
"create_failed": "Kunne ikke oprette etiket",
|
||||
"create_success": "Etiket oprettet",
|
||||
"label_name_too_long": "Etiketnavnet må ikke være længere end 50 tegn"
|
||||
}
|
||||
"title": "Opret label"
|
||||
},
|
||||
"selector": {
|
||||
"select_labels": "Vælg Etiketter"
|
||||
@@ -171,74 +97,47 @@
|
||||
"create_modal": {
|
||||
"location_description": "Lokationsbeskrivelse",
|
||||
"location_name": "Lokationsnavn",
|
||||
"title": "Opret lokation",
|
||||
"toast": {
|
||||
"already_creating": "Allerede oprettet en lokation",
|
||||
"create_failed": "Kunne ikke oprette placering",
|
||||
"create_success": "Placering oprettet"
|
||||
}
|
||||
"title": "Opret lokation"
|
||||
},
|
||||
"selector": {
|
||||
"no_location_found": "Ingen placering fundet",
|
||||
"parent_location": "Forældrelokation",
|
||||
"search_location": "Søg efter placeringer",
|
||||
"select_location": "Vælg en placering"
|
||||
"parent_location": "Forældrelokation"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "Ingen tilgængelige placeringer. Tilføj nye placeringer via knappen \n'<span class=\"link-primary\">'Opret'</span>' på navigationslinjen."
|
||||
"no_locations": "Ingen tilgængelige lokationer. Opret nye lokationer gennem\n`<`span class=\"link-primary\">`Opret`<`/span`>` knappen i navigationslinjen."
|
||||
}
|
||||
},
|
||||
"quick_menu": {
|
||||
"no_results": "Ingen resultater fundet.",
|
||||
"shortcut_hint": "Brug de numeriske taster til hurtigt at vælge en handling."
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"api_failure": "Backend API kald fejlede: "
|
||||
},
|
||||
"global": {
|
||||
"add": "Tilføj",
|
||||
"archived": "Arkiveret",
|
||||
"build": "Build: { build }",
|
||||
"cancel": "Ophæv",
|
||||
"confirm": "Bekræft",
|
||||
"create": "Opret",
|
||||
"create_and_add": "Opret og tilføj ny",
|
||||
"create_subitem": "Opret underelement",
|
||||
"created": "Oprettet",
|
||||
"delete": "Slet",
|
||||
"delete_confirm": "Er du sikker på, at du vil slette dette element? ",
|
||||
"demo_instance": "Dette er en demo-instans",
|
||||
"details": "Detaljer",
|
||||
"duplicate": "Dupliker",
|
||||
"edit": "Rediger",
|
||||
"email": "Email",
|
||||
"follow_dev": "Følg udvikleren",
|
||||
"footer": {
|
||||
"api_link": "'<a href=\"https://homebox.software/en/api/\" target=\"_blank\">'API'</a>'",
|
||||
"version_link": "'<'a href=\"https://github.com/sysadminsmedia/homebox/releases/tag/{ version }\" target=\"_blank\"'>' Version: { version } Build: { build } '</a>'"
|
||||
},
|
||||
"github": "GitHub projekt",
|
||||
"insured": "Forsikret",
|
||||
"items": "Genstande",
|
||||
"join_discord": "Deltag i vores Discord",
|
||||
"labels": "Etiketter",
|
||||
"loading": "Indlæser…",
|
||||
"locations": "Lokationer",
|
||||
"maintenance": "Opretholdelse",
|
||||
"name": "Navn",
|
||||
"navigate": "Naviger",
|
||||
"password": "Adgangskode",
|
||||
"quantity": "Mængde",
|
||||
"read_docs": "Læs Docs",
|
||||
"return_home": "Vend hjem",
|
||||
"save": "Gem",
|
||||
"search": "Søg",
|
||||
"sign_out": "Log ud",
|
||||
"submit": "Indsend",
|
||||
"unknown": "Ukendt",
|
||||
"update": "Opdater",
|
||||
"updating": "Opdaterer",
|
||||
"value": "Værdi",
|
||||
"version": "Version: { version }",
|
||||
"welcome": "Velkommen, { username }"
|
||||
@@ -263,49 +162,27 @@
|
||||
"set_email": "Hvad er din E-Mail?",
|
||||
"set_name": "Hvad hedder du?",
|
||||
"set_password": "Opret din adgangskode",
|
||||
"tagline": "Følg, Organiser, og Håndter dine Ting.",
|
||||
"title": "Organiser og Tag dine ting",
|
||||
"toast": {
|
||||
"invalid_email": "Ugyldig e-mailadresse",
|
||||
"invalid_email_password": "Ugyldig e-mail eller adgangskode",
|
||||
"login_success": "Logget ind",
|
||||
"problem_registering": "Problem med at registrere bruger",
|
||||
"user_registered": "Bruger registreret"
|
||||
}
|
||||
"tagline": "Følg, Organiser, og Håndter dine Ting."
|
||||
},
|
||||
"items": {
|
||||
"add": "Tilføj",
|
||||
"advanced": "Avanceret",
|
||||
"archived": "Arkiveret",
|
||||
"asset_id": "Aktiv-id",
|
||||
"associated_with_multiple": "Dette aktiv-id er knyttet til flere varer",
|
||||
"attachment": "Vedhæftning",
|
||||
"attachments": "Vedhæftninger",
|
||||
"changes_persisted_immediately": "Ændringer af vedhæftede filer gemmes med det samme",
|
||||
"created_at": "Oprettet den",
|
||||
"custom_fields": "Brugerdefinerede felter",
|
||||
"delete_attachment_confirm": "Er du sikker på, at du vil slette denne vedhæftede fil?",
|
||||
"delete_item_confirm": "Er du sikker på, at du vil slette dette element?",
|
||||
"description": "Beskrivelse",
|
||||
"details": "Detaljer",
|
||||
"drag_and_drop": "Træk og slip filer her, eller klik for at vælge filer",
|
||||
"edit": {
|
||||
"edit_attachment_dialog": {
|
||||
"attachment_title": "Titel på vedhæftet fil",
|
||||
"attachment_type": "Vedhæftningstype",
|
||||
"primary_photo": "Primært foto",
|
||||
"primary_photo_sub": "Denne mulighed er kun tilgængelig for fotos. Kun ét foto kan være primært. Hvis du vælger denne mulighed, vil det aktuelle primære foto, hvis der er et, blive fravalgt.",
|
||||
"select_type": "Vælg en type",
|
||||
"title": "Rediger vedhæftet fil"
|
||||
}
|
||||
},
|
||||
"edit_details": "Rediger detaljer",
|
||||
"field_selector": "Feltvælger",
|
||||
"field_value": "Feltværdi",
|
||||
"first": "Første",
|
||||
"include_archive": "Medtag arkiverede elementer",
|
||||
"insured": "Forsikret",
|
||||
"invalid_asset_id": "Ugyldigt aktiv-ID",
|
||||
"last": "Sidst",
|
||||
"lifetime_warranty": "livstidsgaranti",
|
||||
"location": "Lokalitet",
|
||||
@@ -316,7 +193,6 @@
|
||||
"name": "Navn",
|
||||
"negate_labels": "Ophæv valgte etiketter",
|
||||
"next_page": "Næste side",
|
||||
"no_attachments": "Ingen vedhæftede filer fundet",
|
||||
"no_results": "Ingen elementer fundet",
|
||||
"notes": "Noter",
|
||||
"only_with_photo": "Kun elementer med foto",
|
||||
@@ -338,77 +214,35 @@
|
||||
"receipts": "Kvitteringer",
|
||||
"reset_search": "Nulstil Søgning",
|
||||
"results": "{ total } Wyniki",
|
||||
"select_field": "Vælg et felt",
|
||||
"serial_number": "Serienummer",
|
||||
"show_advanced_view_options": "vis avancerede indstillinger",
|
||||
"sold_at": "Solgt D.",
|
||||
"sold_details": "Salgs detaljer",
|
||||
"sold_price": "Solgt pris",
|
||||
"sold_to": "Sold til",
|
||||
"sync_child_locations": "Synkroniser placeringer af underordnede elementer",
|
||||
"tip_1": "Placerings- og etiketfiltre bruger betjeningen 'ELLER'. Hvis mere end én er valgt, kræves der kun én\n til et match.",
|
||||
"tip_2": "Søgninger med præfikset '#'' vil forespørge efter et aktiv-id (eksempel '#000-001')",
|
||||
"tip_3": "Feltfiltre bruger handlingen 'ELLER'. Hvis mere end én er valgt, kræves der kun én til en\n kamp.",
|
||||
"tips": "Tips",
|
||||
"tips_sub": "Søgetips",
|
||||
"toast": {
|
||||
"asset_not_found": "Aktivet blev ikke fundet",
|
||||
"attachment_deleted": "Vedhæftet fil slettet",
|
||||
"attachment_updated": "Vedhæftet fil opdateret",
|
||||
"attachment_uploaded": "Vedhæftet fil uploadet",
|
||||
"child_items_location_no_longer_synced": "Placeringen af underelementer vil ikke længere blive synkroniseret med dette element.",
|
||||
"child_items_location_synced": "Placeringerne af underordnede elementer er blevet synkroniseret med dette element",
|
||||
"child_location_desync": "Ændring af placering vil afsynkronisere den fra forælderens placering",
|
||||
"error_loading_parent_data": "Noget gik galt under indlæsning af overordnede data",
|
||||
"failed_adjust_quantity": "Kunne ikke justere mængden",
|
||||
"failed_delete_attachment": "Kunne ikke slette vedhæftet fil",
|
||||
"failed_delete_item": "Kunne ikke slette elementet",
|
||||
"failed_duplicate_item": "Kunne ikke duplikere elementet",
|
||||
"failed_load_asset": "Kunne ikke indlæse aktiv",
|
||||
"failed_load_item": "Kunne ikke indlæse elementet",
|
||||
"failed_load_items": "Kunne ikke indlæse elementer",
|
||||
"failed_save": "Kunne ikke gemme elementet",
|
||||
"failed_save_no_location": "Kunne ikke gemme elementet: ingen placering valgt",
|
||||
"failed_search_items": "Kunne ikke søge efter elementer",
|
||||
"failed_update_attachment": "Kunne ikke opdatere vedhæftet fil",
|
||||
"failed_upload_attachment": "Kunne ikke uploade vedhæftet fil",
|
||||
"item_deleted": "Element slettet",
|
||||
"item_saved": "Element gemt",
|
||||
"quantity_cannot_negative": "Mængden må ikke være negativ",
|
||||
"sync_child_location": "Den valgte forælder synkroniserer sine børns placeringer med sine egne. Placeringen er blevet opdateret."
|
||||
},
|
||||
"updated_at": "Opdateret d.",
|
||||
"warranty": "Garanti",
|
||||
"warranty_details": "Oplysninger om garanti",
|
||||
"warranty_expires": "Garantien udløber"
|
||||
},
|
||||
"labels": {
|
||||
"label_delete_confirm": "Er du sikker på, at du vil slette denne etiket? Denne handling kan ikke fortrydes.",
|
||||
"no_results": "Ingen etiketter fundet",
|
||||
"toast": {
|
||||
"failed_delete_label": "Etiketten kunne ikke slettes",
|
||||
"failed_load_label": "Etiketten kunne ikke indlæses",
|
||||
"failed_update_label": "Etiketten kunne ikke opdateres",
|
||||
"label_deleted": "Etiket slettet",
|
||||
"label_updated": "Etiket opdateret"
|
||||
},
|
||||
"update_label": "Opdater etiket"
|
||||
},
|
||||
"languages": {
|
||||
"ca": "Catalansk",
|
||||
"cs-CZ": "Tjekkisk",
|
||||
"de": "Tysk",
|
||||
"en": "Engelsk",
|
||||
"es": "Spansk",
|
||||
"fi-FI": "Finsk",
|
||||
"fr": "Fransk",
|
||||
"hu": "Ungarsk",
|
||||
"id-ID": "Indonesisk",
|
||||
"it": "Italiensk",
|
||||
"ja-JP": "Japansk",
|
||||
"ko-KR": "Koreansk",
|
||||
"lb-LU": "Luxembourgsk (Luxembourg)",
|
||||
"lt-LT": "Litauisk (Litauen)",
|
||||
"nb-NO": "Norsk",
|
||||
"nl": "Hollandsk",
|
||||
"pl": "Polsk",
|
||||
@@ -416,7 +250,6 @@
|
||||
"pt-PT": "Portugisisk (Portugal)",
|
||||
"ru": "Russisk",
|
||||
"sl": "Slovensk",
|
||||
"sq-AL": "Albansk",
|
||||
"sv": "Svensk",
|
||||
"ta-IN": "Tamilsk",
|
||||
"th-TH": "Thailandsk",
|
||||
@@ -433,16 +266,7 @@
|
||||
"locations": {
|
||||
"child_locations": "Underordnede placeringer",
|
||||
"collapse_tree": "Kollaps træ",
|
||||
"expand_tree": "Udvid træ",
|
||||
"location_items_delete_confirm": "Er du sikker på, at du vil slette denne placering og alle dens elementer? Denne handling kan ikke fortrydes.",
|
||||
"no_results": "Ingen placeringer fundet",
|
||||
"toast": {
|
||||
"failed_delete_location": "Kunne ikke slette placeringen",
|
||||
"failed_load_location": "Placeringen kunne ikke indlæses",
|
||||
"failed_update_location": "Kunne ikke opdatere placeringen",
|
||||
"location_deleted": "Placering slettet",
|
||||
"location_updated": "Placering opdateret"
|
||||
},
|
||||
"update_location": "Opdatér sted"
|
||||
},
|
||||
"maintenance": {
|
||||
@@ -498,10 +322,7 @@
|
||||
"currency_format": "Valuta format",
|
||||
"current_password": "Aktuel adgangskode",
|
||||
"delete_account": "Slet Konto",
|
||||
"delete_account_confirm": "Er du sikker på, at du vil slette din konto? Hvis du er det sidste medlem i din gruppe, vil alle dine data blive slettet. Denne handling kan ikke fortrydes.",
|
||||
"delete_account_sub": "Slet din konto og alle dens tilknyttede data. Dette kan ikke laves om.",
|
||||
"delete_notifier_confirm": "Er du sikker på, at du vil slette denne underretter?",
|
||||
"display_legacy_header": "{ currentValue, select, true {Deaktiver Legacy Header} false {Aktiver Legacy Header} other {Ikke ramt}}",
|
||||
"enabled": "Aktiveret",
|
||||
"example": "Eksempel",
|
||||
"gen_invite": "Generer invitationslink",
|
||||
@@ -511,97 +332,39 @@
|
||||
"language": "Sprog",
|
||||
"new_password": "Ny Adgangskode",
|
||||
"no_notifiers": "Ingen notifikationer konfiguret",
|
||||
"no_override": "Ingen tilsidesættelse",
|
||||
"notifier_modal": "{ type, select, true {Rediger} false {Opret} other {Andet}} Meddeler",
|
||||
"notifiers": "Meddelere",
|
||||
"notifiers_sub": "Få notifikationer om kommende vedligeholdelsespåmindelser",
|
||||
"override_locale": "Tilsidesæt dato og valutasprog",
|
||||
"test": "Test",
|
||||
"theme_settings": "Temaindstillinger",
|
||||
"theme_settings_sub": "Temaindstillinger gemmes i din browsers lokale lager. Du kan til enhver tid ændre temaet. Hvis du har\n problemer med at indstille dit tema, kan du prøve at opdatere din browser.",
|
||||
"toast": {
|
||||
"account_deleted": "Din konto er blevet slettet.",
|
||||
"failed_change_password": "Kunne ikke ændre adgangskode.",
|
||||
"failed_create_notifier": "Kunne ikke oprette underretteren.",
|
||||
"failed_delete_account": "Kunne ikke slette din konto.",
|
||||
"failed_delete_notifier": "Kunne ikke slette underretteren.",
|
||||
"failed_get_currencies": "Kunne ikke hente valutaer",
|
||||
"failed_test_notifier": "Kunne ikke teste underretteren.",
|
||||
"failed_update_group": "Gruppen kunne ikke opdateres",
|
||||
"failed_update_notifier": "Kunne ikke opdatere underretteren.",
|
||||
"group_updated": "Gruppen er opdateret",
|
||||
"notifier_test_success": "Underretter-testen er gennemført.",
|
||||
"password_changed": "Adgangskoden er ændret."
|
||||
},
|
||||
"update_group": "Opdatér Gruppe",
|
||||
"update_language": "Opdater sprogfil",
|
||||
"url": "URL",
|
||||
"user_profile": "Brugerprofil",
|
||||
"user_profile_sub": "Inviter brugere, og administrer din konto."
|
||||
},
|
||||
"reports": {
|
||||
"label_generator": {
|
||||
"asset_end": "Aktivets slut",
|
||||
"asset_start": "Aktivets start",
|
||||
"base_url": "Base URL",
|
||||
"bordered_labels": "Etiketter med kant",
|
||||
"generate_page": "Generer side",
|
||||
"input_placeholder": "Skriv her",
|
||||
"instruction_1": "Homebox Label Generator er et værktøj, der hjælper dig med at udskrive etiketter til dit Homebox-lager. Disse er beregnet til\nat være etiketter, der kan udskrives på forhånd, så du kan udskrive mange etiketter og have dem klar til påsætning.",
|
||||
"instruction_2": "Disse etiketter fungerer derfor ved at udskrive en URL, QR-kode og AssetID-oplysninger på en etiket. Hvis du har deaktiveret\nAssetID'er i dine Homebox-indstillinger, kan du stadig bruge dette værktøj, men AssetID'erne vil ikke referere til nogen varer.",
|
||||
"instruction_3": "Denne funktion er i de tidlige udviklingsfaser og kan ændres i fremtidige udgivelser. Hvis du har feedback, bedes\ndu give den i '<a href=\"https://github.com/sysadminsmedia/homebox/discussions/53\">'GitHub-diskussionen'</a>'",
|
||||
"label_height": "Etikethøjde",
|
||||
"label_width": "Etiketbredde",
|
||||
"measure_type": "Målingstype",
|
||||
"page_bottom_padding": "Sidebundspolstring",
|
||||
"page_height": "Sidehøjde",
|
||||
"page_left_padding": "Venstre sidepolstring",
|
||||
"page_right_padding": "Højre sidepolstring",
|
||||
"page_top_padding": "Sidetoppolstring",
|
||||
"page_width": "Sidebredde",
|
||||
"qr_code_example": "Eksempel på QR-kode",
|
||||
"tip_1": "Standardindstillingerne her er konfigureret for\n'<a href=\"https://www.avery.com/templates/5260\">'Avery 5260 etiketark'</a>'. Hvis du bruger et andet ark,\nskal du justere indstillingerne, så de passer til dit ark.",
|
||||
"tip_2": "Hvis du tilpasser dit ark, er dimensionerne i tommer. Da jeg byggede 5260-arket, opdagede jeg, at de\ndimensioner, der blev brugt i deres skabelon, ikke matchede det, der var nødvendigt for at udskrive i felterne.\n'<b>'Vær forberedt på nogle forsøg og fejl.'</b>'",
|
||||
"tip_3": "Ved printning sørg for at:\n'<ol><li>'Indstil margenerne til 0 eller Ingen'</li><li>'Indstil skaleringen til 100%'</li><li>'Deaktiver dobbeltsidet udskrivning'</li><li>'Udskriv en testside, før du udskriver flere sider'</li></ol>'",
|
||||
"tips": "Tips",
|
||||
"title": "Etiketgenerator",
|
||||
"toast": {
|
||||
"page_too_small_card": "Sidestørrelsen er for lille til kortstørrelsen"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
"barcode_detected_message": "produkt stregkode opdaget",
|
||||
"barcode_fetch_data": "Hent produktdata",
|
||||
"error": "Der skete en fejl under skanningen",
|
||||
"invalid_url": "Ugyldig stregkode-URL",
|
||||
"no_sources": "Ingen videokilder er tilgængelig",
|
||||
"permission_denied": "Kameratilladelse nægtet, Tillad venligst adgang til kameraet i dine browserindstillinger",
|
||||
"select_video_source": "Vælg en videokilde",
|
||||
"title": "Skanner",
|
||||
"unsupported": "Media Stream API understøttes ikke uden HTTPS"
|
||||
},
|
||||
"tools": {
|
||||
"actions": "Handlinger på lagerbeholdning",
|
||||
"actions_set": {
|
||||
"create_missing_thumbnails": "Opret manglende miniaturebilleder",
|
||||
"create_missing_thumbnails_button": "Opret miniaturebilleder",
|
||||
"create_missing_thumbnails_confirm": "Er du sikker på, at du vil oprette manglende miniaturebilleder? Dette kan tage et stykke tid og kan ikke sættes på pause.",
|
||||
"create_missing_thumbnails_sub": "Opretter miniaturebilleder for alle vedhæftede filer, der understøttes af den aktuelle konfiguration. Dette er nyttigt for vedhæftede filer, der blev uploadet før v0.20.0-udgivelsen af Homebox. Dette overskriver ikke eksisterende miniaturebilleder, men opretter kun nye for vedhæftede filer, der ikke har et miniaturebillede. Bemærk, at miniaturebillederne oprettes i baggrunden og kan tage et stykke tid at fuldføre.",
|
||||
"ensure_ids": "Sørg for aktiv-id'er",
|
||||
"ensure_ids_button": "Sørg for aktiv-id'er",
|
||||
"ensure_ids_confirm": "Er du sikker på, at du vil sikre dig, at alle aktiver har et ID? Dette kan tage et stykke tid og kan ikke fortrydes.",
|
||||
"ensure_ids_sub": "Sikrer, at alle varer på lageret har et gyldigt asset_id felt. Dette gøres ved at finde det højeste aktuelle aktiv_id felt i databasen og anvende den næste værdi på hvert element, der har et ikke sat aktiv_id felt. Dette gøres i rækkefølge efter feltet opret_den.",
|
||||
"ensure_import_refs": "Sørg for importreferencer",
|
||||
"ensure_import_refs_button": "Sørg for importreferencer",
|
||||
"ensure_import_refs_sub": "Sikrer, at alle varer på lageret har et gyldigt import_ref felt. Dette gøres ved tilfældigt at generere en streng på 8 tegn for hvert element, der har et uindstillet import_ref felt.",
|
||||
"set_primary_photo": "Indstil primært foto",
|
||||
"set_primary_photo_button": "Indstil primært foto",
|
||||
"set_primary_photo_confirm": "Er du sikker på, at du vil indstille primære billeder? Dette kan tage et stykke tid og kan ikke fortrydes.",
|
||||
"set_primary_photo_sub": "I version v0.10.0 af Homebox blev det primære billedfelt tilføjet til vedhæftede filer af typen foto. Denne handling indstiller det primære billedfelt til det første billede i matrixen for vedhæftede filer i databasen, hvis det ikke allerede er angivet. '<a class=\"link\" href=\"https://github.com/hay-kot/homebox/pull/576\">'Se GitHub PR #576'</a>'",
|
||||
"zero_datetimes": "Nul Vare Dato Tider",
|
||||
"zero_datetimes_button": "Nul Varedato Tider",
|
||||
"zero_datetimes_confirm": "Er du sikker på, at du vil nulstille alle dato- og klokkeslætsværdier? Dette kan tage et stykke tid og kan ikke fortrydes.",
|
||||
"zero_datetimes_sub": "Nulstiller klokkeslætsværdien for alle dato- og klokkeslætsfelter i lageret til begyndelsen af datoen. Dette er for at rette en fejl, der blev introduceret tidligt i udviklingen af webstedet, der forårsagede, at tidsværdien blev gemt med tiden, hvilket forårsagede problemer med datofelter, der viste nøjagtige værdier. '<a class=\"link\" href=\"https://github.com/hay-kot/homebox/issues/236\" target=\"_blank\">'Se Github-udgave #236 for flere detaljer.'</a>'"
|
||||
},
|
||||
"actions_sub": "Anvend flere handlinger på din beholdning på én gang. Det er uigenkaldelige handlinger. '<b>'Vær forsigtig.'</b>'",
|
||||
@@ -612,7 +375,6 @@
|
||||
"export_sub": "Eksporterer standard CSV-formatet til Homebox. Dette vil eksportere alle varer i dit lager.",
|
||||
"import": "Importeret beholdning",
|
||||
"import_button": "Importer beholdning",
|
||||
"import_ref_confirm": "Er du sikker på, at du vil sikre dig, at alle aktiver har en import_ref? Dette kan tage et stykke tid og kan ikke fortrydes.",
|
||||
"import_sub": "Importerer standard CSV-formatet til Homebox. Uden en '<code>'HB.import_ref'</code>'-kolonne vil dette '<b>'ikke'</b>' overskrive eksisterende genstande i dit lager, kun tilføje nye genstande. Rækker med kolonnen \"<code>HB.import_ref\"</code> flettes ind i eksisterende elementer med samme import_ref, hvis der findes en."
|
||||
},
|
||||
"import_export_sub": "Importér og eksporter din lagerbeholdning til og fra en CSV-fil. Dette er nyttigt til at migrere dit lager til en ny forekomst af Homebox.",
|
||||
@@ -625,14 +387,6 @@
|
||||
"bill_of_materials_button": "Generer stykliste",
|
||||
"bill_of_materials_sub": "Genererer en CSV-fil (kommaseparerede værdier), der kan importeres til et regnearksprogram. Dette er en oversigt over din beholdning med grundlæggende vare- og prisoplysninger."
|
||||
},
|
||||
"reports_sub": "Generer forskellige rapporter for dit lager.",
|
||||
"toast": {
|
||||
"asset_success": "Aktiverne i { results } er blevet opdateret.",
|
||||
"failed_create_missing_thumbnails": "Kunne ikke oprette manglende miniaturebilleder.",
|
||||
"failed_ensure_ids": "Kunne ikke sikre aktiv-ID'er.",
|
||||
"failed_ensure_import_refs": "Kunne ikke sikre importreferencer.",
|
||||
"failed_set_primary_photos": "Kunne ikke indstille primære billeder.",
|
||||
"failed_zero_datetimes": "Dato- og klokkeslætsværdier kunne ikke nulstilles."
|
||||
}
|
||||
"reports_sub": "Generer forskellige rapporter for dit lager."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"components": {
|
||||
"app": {
|
||||
"create_modal": {
|
||||
"createAndAddAnother": "Verwenden Sie {shiftKey} + {enterKey}, um eine weitere zu erstellen und hinzuzufügen.",
|
||||
"createAndAddAnother": "Verwenden Sie {Umschalttaste} + {Eingabetaste}, um eine weitere zu erstellen und hinzuzufügen.",
|
||||
"enter": "Eingabe",
|
||||
"shift": "Shift"
|
||||
},
|
||||
@@ -28,8 +28,7 @@
|
||||
"clear": "Farbe löschen",
|
||||
"color": "Farbe",
|
||||
"no_color": "Keine Farbe",
|
||||
"no_color_selected": "Keine Farbe ausgewählt",
|
||||
"randomize": "Zufällige Farbe"
|
||||
"no_color_selected": "Keine Farbe ausgewählt"
|
||||
},
|
||||
"form": {
|
||||
"password": {
|
||||
@@ -100,8 +99,6 @@
|
||||
"item_photo": "Artikel Bild",
|
||||
"item_quantity": "Anzahl der Artikel",
|
||||
"parent_item": "Übergeordneter Gegenstand",
|
||||
"product_tooltip_input_barcode": "Automatisch ausfüllen mit einem manuell bereitgestellten Barcode",
|
||||
"product_tooltip_scan_barcode": "Automatisch ausfüllen mit einem Barcode von 📷",
|
||||
"rotate_photo": "Photo drehen",
|
||||
"set_as_primary_photo": "Festlegen als { isPrimary, select, true {non-} false {} other {}}primäres Foto",
|
||||
"title": "Gegenstand erstellen",
|
||||
@@ -114,27 +111,18 @@
|
||||
"please_select_location": "Bitte einen Ort auswählen.",
|
||||
"rotate_failed": "Drehen des Bildes fehlgeschlagen: {error}",
|
||||
"rotate_process_failed": "Das gedrehte Bild konnte nicht verarbeitet werden",
|
||||
"some_photos_failed": "{count, plural, =0 {Keine Fotos zum Hochladen.} =1 {1 Foto konnte nicht hochgeladen werden.} other {Einige Fotos konnten nicht hochgeladen werden.}}",
|
||||
"some_photos_failed": "{Anzahl, Plural, =0 {Keine Fotos zum Hochladen.} =1 {1 Foto konnte nicht hochgeladen werden.} andere {Einige Fotos konnten nicht hochgeladen werden.}}",
|
||||
"upload_failed": "Hochladen des Bildes Fehlgeschlagen: { photoName }",
|
||||
"upload_success": "{count, plural, =0 {Keine Fotos hochgeladen.} =1 {Foto erfolgreich hochgeladen.} other {Alle Fotos erfolgreich hochgeladen.}}",
|
||||
"uploading_photos": "{count, plural, =0 {Keine Fotos zum Hochladen} =1 {1 Foto wird hochgeladen...} other {{count} Fotos werden hochgeladen...}}"
|
||||
"upload_success": "{Anzahl, plural, =0 {Keine Fotos hochgeladen.} =1 {Foto erfolgreich hochgeladen.} other {Alle Fotos erfolgreich hochgeladen.}}",
|
||||
"uploading_photos": "{Anzahl, plural, =0 {Keine Fotos zum Hochladen} =1 {1 Foto wird hochgeladen...} other {{Anzahl} Fotos werden hochgeladen...}}"
|
||||
},
|
||||
"upload_photos": "Upload Bilder",
|
||||
"uploaded": "Bild hochgeladen"
|
||||
},
|
||||
"product_import": {
|
||||
"barcode": "Produkt-Strichcode",
|
||||
"db_source": "DB-Quelle",
|
||||
"error_exception": "Beim Abrufen des Artikel-Barcodes ist ein Problem aufgetreten: ",
|
||||
"error_invalid_barcode": "Ungültiger Barcode angegeben",
|
||||
"error_not_found": "Kein Produkt mit dem angegebenen Barcode gefunden.",
|
||||
"search_item": "Produkt suchen",
|
||||
"title": "Produkt importieren"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Keine Ergebnisse gefunden",
|
||||
"placeholder": "Auswählen…",
|
||||
"search_placeholder": "Für Suche tippen…"
|
||||
"placeholder": "Auswählen...",
|
||||
"search_placeholder": "Für Suche tippen..."
|
||||
},
|
||||
"view": {
|
||||
"selectable": {
|
||||
@@ -154,7 +142,6 @@
|
||||
},
|
||||
"label": {
|
||||
"create_modal": {
|
||||
"label_color": "Label-Farbe",
|
||||
"label_description": "Label-Beschreibung",
|
||||
"label_name": "Label-Name",
|
||||
"title": "Label erstellen",
|
||||
@@ -187,7 +174,7 @@
|
||||
"select_location": "Standort wählen"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "Keine Standorte verfügbar. Fügen Sie neue Standorte über die Schaltfläche\n `<span class=\"link-primary\">`Erstellen`</span>` in der Navigationsleiste hinzu."
|
||||
"no_locations": "Keine Standorte verfügbar. Fügen Sie neue Standorte über die Schaltfläche\n `<`span class=\"link-primary\"`>`Erstellen`<`/span`>` in der Navigationsleiste hinzu."
|
||||
}
|
||||
},
|
||||
"quick_menu": {
|
||||
@@ -195,9 +182,6 @@
|
||||
"shortcut_hint": "Verwenden Sie die Zifferntasten, um schnell eine Aktion auszuwählen."
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"api_failure": "Backend-API-Aufruf fehlgeschlagen: "
|
||||
},
|
||||
"global": {
|
||||
"add": "Hinzufügen",
|
||||
"archived": "Archiviert",
|
||||
@@ -225,7 +209,7 @@
|
||||
"items": "Gegenstände",
|
||||
"join_discord": "Discord beitreten",
|
||||
"labels": "Labels",
|
||||
"loading": "Wird geladen…",
|
||||
"loading": "Wird geladen …",
|
||||
"locations": "Lagerorte",
|
||||
"maintenance": "Wartung",
|
||||
"name": "Name",
|
||||
@@ -397,7 +381,6 @@
|
||||
"update_label": "Label aktualisieren"
|
||||
},
|
||||
"languages": {
|
||||
"bs-BA": "Bosnisch (Bosnien und Herzegowina)",
|
||||
"ca": "Katalanisch",
|
||||
"cs-CZ": "Tschechisch",
|
||||
"de": "Deutsch",
|
||||
@@ -424,10 +407,9 @@
|
||||
"th-TH": "Thailändisch",
|
||||
"tr": "Türkisch",
|
||||
"uk-UA": "Ukrainisch",
|
||||
"vi-VN": "Vietnamesisch",
|
||||
"zh-CN": "Chinesisch (vereinfacht)",
|
||||
"zh-CN": "Chinesisch (einfach)",
|
||||
"zh-HK": "Chinesisch (Hong Kong)",
|
||||
"zh-MO": "Chinesisch (Macau)",
|
||||
"zh-MO": "Chinesisch (Macao)",
|
||||
"zh-TW": "Chinesisch (traditionell)"
|
||||
},
|
||||
"languages.da-DK": "Dänisch",
|
||||
@@ -439,7 +421,7 @@
|
||||
"collapse_tree": "Baum einklappen",
|
||||
"expand_tree": "Baum ausklappen",
|
||||
"location_items_delete_confirm": "Möchten Sie diesen Standort und alle darin enthaltenen Elemente wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"no_results": "Keine Standorte gefunden",
|
||||
"no_results": "Keine Orte gefunden",
|
||||
"toast": {
|
||||
"failed_delete_location": "Standort konnte nicht gelöscht werden",
|
||||
"failed_load_location": "Standort konnte nicht geladen werden",
|
||||
@@ -565,7 +547,7 @@
|
||||
"page_width": "Seitenbreite",
|
||||
"qr_code_example": "QR-Code Beispiel",
|
||||
"tip_1": "Die Standardeinstellungen hier sind für die\n'<a href=\"https://www.avery.com/templates/5260\">'Avery 5260 Etikettenbögen'</a>'. Wenn Sie einen anderen Bogen verwenden,\nmüssen Sie die Einstellungen an Ihr Blatt anpassen.",
|
||||
"tip_2": "Wenn Sie Ihr Blatt anpassen, werden die Abmessungen in Zoll angegeben. Beim Erstellen des 5260-Blattes habe ich festgestellt, dass die\nin deren Vorlage verwendeten Abmessungen nicht mit den für den Druck in den Feldern erforderlichen Abmessungen übereinstimmen.\n'<b>'Seien Sie auf einige Versuche und Irrtümer gefasst.'</b>'",
|
||||
"tip_2": "Wenn Sie Ihr Blatt anpassen, werden die Abmessungen in Zoll angegeben. Beim Erstellen des 5260-Blattes habe ich festgestellt, dass die\nin deren Vorlage verwendeten Abmessungen nicht mit den für den Druck in den Feldern erforderlichen Abmessungen übereinstimmen.\n„<b>Seien Sie auf einige Versuche und Irrtümer gefasst.“</b>",
|
||||
"tip_3": "Achten Sie beim Drucken auf Folgendes:\n'<ol><li>'Setzen Sie die Ränder auf 0 oder Keine'</li><li>'Setzen Sie die Skalierung auf 100 %'</li><li>'Deaktivieren Sie den beidseitigen Druck'</li><li>'Drucken Sie eine Testseite, bevor Sie mehrere Seiten drucken'</li></ol>'",
|
||||
"tips": "Tipps",
|
||||
"title": "Etikettengenerator",
|
||||
@@ -575,8 +557,6 @@
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
"barcode_detected_message": "Produkt-Barcode erkannt",
|
||||
"barcode_fetch_data": "Produktdaten abrufen",
|
||||
"error": "Beim Scannen ist ein Fehler aufgetreten",
|
||||
"invalid_url": "Ungültige Barcode-URL",
|
||||
"no_sources": "Keine Videoquellen verfügbar",
|
||||
|
||||
@@ -100,8 +100,6 @@
|
||||
"item_photo": "Item Photo 📷",
|
||||
"item_quantity": "Item Quantity",
|
||||
"parent_item": "Parent Item",
|
||||
"product_tooltip_input_barcode": "Autofill with a manually provided barcode",
|
||||
"product_tooltip_scan_barcode": "Autofill with a barcode from 📷",
|
||||
"rotate_photo": "Rotate photo",
|
||||
"set_as_primary_photo": "Set as { isPrimary, select, true {non-} false {} other {}}primary photo",
|
||||
"title": "Create Item",
|
||||
@@ -122,15 +120,6 @@
|
||||
"upload_photos": "Upload Photos",
|
||||
"uploaded": "Uploaded Photo"
|
||||
},
|
||||
"product_import": {
|
||||
"barcode": "Product's barcode",
|
||||
"db_source": "DB source",
|
||||
"error_exception": "Exception occured while retrieving item barcode: ",
|
||||
"error_invalid_barcode": "Invalid barcode provided",
|
||||
"error_not_found": "No product found with given barcode.",
|
||||
"search_item": "Search product",
|
||||
"title": "Import product"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "No Results Found",
|
||||
"placeholder": "Select…",
|
||||
@@ -195,9 +184,6 @@
|
||||
"shortcut_hint": "Use the number keys to quickly select an action."
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"api_failure": "Backend API call failed: "
|
||||
},
|
||||
"global": {
|
||||
"add": "Add",
|
||||
"archived": "Archived",
|
||||
@@ -289,18 +275,6 @@
|
||||
"delete_attachment_confirm": "Are you sure you want to delete this attachment?",
|
||||
"delete_item_confirm": "Are you sure you want to delete this item?",
|
||||
"description": "Description",
|
||||
"duplicate": {
|
||||
"prefix": "Copy of ",
|
||||
"copy_maintenance": "Copy Maintenance",
|
||||
"copy_attachments": "Copy Attachments",
|
||||
"copy_custom_fields": "Copy Custom Fields",
|
||||
"custom_prefix": "Copy Prefix",
|
||||
"enable_custom_prefix": "Enable Custom Prefix",
|
||||
"prefix_instructions": "This prefix will be added to the beginning of the duplicated item's name. Include a space at the end of the prefix to add a space between the prefix and the item name.",
|
||||
"temporary_title": "Temporary Settings",
|
||||
"title": "Duplicate Settings",
|
||||
"override_instructions": "Hold shift when clicking the duplicate button to override these settings."
|
||||
},
|
||||
"details": "Details",
|
||||
"drag_and_drop": "Drag and drop files here or click to select files",
|
||||
"edit": {
|
||||
@@ -311,8 +285,7 @@
|
||||
"primary_photo_sub": "This option is only available for photos. Only one photo can be primary. If you select this option, the current primary photo, if any will be unselected.",
|
||||
"select_type": "Select a type",
|
||||
"title": "Attachment Edit"
|
||||
},
|
||||
"view_image": "View Image"
|
||||
}
|
||||
},
|
||||
"edit_details": "Edit Details",
|
||||
"field_selector": "Field Selector",
|
||||
@@ -410,7 +383,6 @@
|
||||
"update_label": "Update Label"
|
||||
},
|
||||
"languages": {
|
||||
"bs-BA": "Bosnian (Bosnia and Herzegovina)",
|
||||
"ca": "Catalan",
|
||||
"cs-CZ": "Czech",
|
||||
"da-DK": "Danish",
|
||||
@@ -441,7 +413,6 @@
|
||||
"th-TH": "Thai",
|
||||
"tr": "Turkish",
|
||||
"uk-UA": "Ukrainian",
|
||||
"vi-VN": "Vietnamese",
|
||||
"zh-CN": "Chinese (Simplified)",
|
||||
"zh-HK": "Chinese (Hong Kong)",
|
||||
"zh-MO": "Chinese (Macau)",
|
||||
@@ -588,8 +559,6 @@
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
"barcode_detected_message": "product barcode detected",
|
||||
"barcode_fetch_data": "Fetch product data",
|
||||
"error": "An error occurred while scanning",
|
||||
"invalid_url": "Invalid barcode URL",
|
||||
"no_sources": "No video sources available",
|
||||
|
||||
@@ -100,8 +100,6 @@
|
||||
"item_photo": "Foto del artículo 📷",
|
||||
"item_quantity": "Cantidad de Elementos",
|
||||
"parent_item": "Elemento Padre",
|
||||
"product_tooltip_input_barcode": "Autocompletar con un código de barras proporcionado manualmente",
|
||||
"product_tooltip_scan_barcode": "Autocompletar con un código de barras desde 📷",
|
||||
"rotate_photo": "Girar foto",
|
||||
"set_as_primary_photo": "Establecer como { isPrimary, select, true {non-} false {} other {}} foto principal",
|
||||
"title": "Crear Elemento",
|
||||
@@ -122,15 +120,6 @@
|
||||
"upload_photos": "Fotos Subidas",
|
||||
"uploaded": "Foto Subida"
|
||||
},
|
||||
"product_import": {
|
||||
"barcode": "Código de barras del producto",
|
||||
"db_source": "Fuente de la base de datos",
|
||||
"error_exception": "Se ha producido una excepción al recuperar el código de barras del artículo: ",
|
||||
"error_invalid_barcode": "Código de barras proporcionado no válido",
|
||||
"error_not_found": "No se ha encontrado ningún producto con código de barras.",
|
||||
"search_item": "Buscar producto",
|
||||
"title": "Importar producto"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Resultados No Encontrados",
|
||||
"placeholder": "Seleccionar…",
|
||||
@@ -195,9 +184,6 @@
|
||||
"shortcut_hint": "Usa las teclas numéricas para seleccionar rápidamente una acción."
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"api_failure": "Error en la llamada a la API del backend: "
|
||||
},
|
||||
"global": {
|
||||
"add": "Añadir",
|
||||
"archived": "Archivado",
|
||||
@@ -551,7 +537,7 @@
|
||||
"input_placeholder": "Escribe aquí",
|
||||
"instruction_1": "El Generador de Etiquetas Homebox es una herramienta que para ayudarte a imprimir etiquetas para tu inventario Homebox. Están pensadas para\n ser etiquetas de impresión anticipada para que puedas imprimir muchas etiquetas y tenerlas listas para usarlas",
|
||||
"instruction_2": "Como tal, estas etiquetas funcionan imprimiendo un código QR de URL e información ID de Activo en una etiqueta. Si has desactivadod\n ID de Activo en la configuración de tu Homebox, puedes seguir utilizando esta herramienta, pero los IDs de Activo no harán referencia a ningún elemento",
|
||||
"instruction_3": "Esta función se encuentra en las primeras etapas de desarrollo y puede cambiar en futuras versiones. Si tienes algún comentario, indícalo\n en la '<a href=\"https://github.com/sysadminsmedia/homebox/discussions/53\">'Discusión de GitHub'</a>'",
|
||||
"instruction_3": "Esta función se encuentra en las primeras etapas de desarrollo y puede cambiar en futuras versiones. Si tienes algún comentario, indícalo\n en la<a href=\"https://github.com/sysadminsmedia/homebox/discussions/53\"> «Discusión de GitHub»</a>",
|
||||
"label_height": "Altura de la Etiqueta",
|
||||
"label_width": "Ancho de la Etiqueta",
|
||||
"measure_type": "Tipo de Medida",
|
||||
@@ -573,8 +559,6 @@
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
"barcode_detected_message": "código de barras del producto detectado",
|
||||
"barcode_fetch_data": "Obtener datos del producto",
|
||||
"error": "Se ha producido un error mientras se escaneaba",
|
||||
"invalid_url": "URL de código de barras inválido",
|
||||
"no_sources": "No hay fuentes de vídeo disponibles",
|
||||
@@ -601,12 +585,12 @@
|
||||
"set_primary_photo_button": "Establecer Foto Principal",
|
||||
"set_primary_photo_confirm": "¿Estás seguro de que quieres configurar las fotos principales? Esto puede tardar un tiempo y no se puede deshacer.",
|
||||
"set_primary_photo_sub": "En la versión v0.10.0 de Homebox, se añadió el indicador de imagen principal a los ficheros adjuntos de tipo foto. Esta acción establecerá la primera imagen de cada artículo como su imagen principal, si no hay una imagen principal ya definida. '<a class=\"link\" href=\"https://github.com/hay-kot/homebox/pull/576\">'Ver PR #576 en GitHub'</a>'",
|
||||
"zero_datetimes": "Poner a cero las horas de los artículos",
|
||||
"zero_datetimes_button": "Poner a cero las horas de los artículos",
|
||||
"zero_datetimes": "Cero Horas Elementos",
|
||||
"zero_datetimes_button": "Cero Horas Elementos",
|
||||
"zero_datetimes_confirm": "¿Estás seguro de que deseas restablecer todos los valores de fecha y hora? Esto puede tardar un tiempo y no se puede deshacer.",
|
||||
"zero_datetimes_sub": "Restablece el valor de la hora para todos los campos de fecha/hora en tu inventario al principio de esa fecha. Esto se hace para corregir un error que se introdujo al principio del desarrollo de la aplicación, que causó que el valor de la hora se almacenase con la fecha, lo cual produjo problemas al mostrar valores precisos del campo. '<a class=\"link\" href=\"https://github.com/hay-kot/homebox/issues/236\" target=\"_blank\">'Ver el issue #236 de GitHub para más detalles.'</a>'"
|
||||
},
|
||||
"actions_sub": "Aplica Acciones a tu inventario de forma masiva. Estas acciones son irreversibles. '<b>'Ten Cuidado.'</b>'",
|
||||
"actions_sub": "Aplica Acciones a tu inventario de forma masiva. Estas son acciones irreversibles. '<b>'Ten Cuidado.'</b>'",
|
||||
"import_export": "Importar/Exportar",
|
||||
"import_export_set": {
|
||||
"export": "Exportar Inventario",
|
||||
@@ -614,7 +598,7 @@
|
||||
"export_sub": "Exporta el formato CSV estándar para Homebox. Esto exportará todos los elementos de tu inventario.",
|
||||
"import": "Importar Inventario",
|
||||
"import_button": "Importar Inventario",
|
||||
"import_ref_confirm": "¿Estás seguro de que deseas asegurarte de que todos los activos tengan un import_ref? Esto puede tardar un tiempo y no se puede deshacer.",
|
||||
"import_ref_confirm": "¿Estás seguro de que deseas asegurarse de que todos los activos tengan un import_ref? Esto puede tardar un tiempo y no se puede deshacer.",
|
||||
"import_sub": "Importa el formato CSV estándar para Homebox. Sin una columna '<code>'HB.import_ref'</code>', esto '<b>'no'</b>' sobrescribirá cualquier elemento existente en tu inventario, sólo añadirá nuevos artículos. Las filas con una columna '<code>'HB.import_ref'</code>' se fusionan con los artículos existentes con la misma import_ref, si existe."
|
||||
},
|
||||
"import_export_sub": "Importa y exporta tu inventario a y desde un archivo CSV. Esto es útil para migrar tu inventario a una nueva instancia de Homebox.",
|
||||
@@ -624,7 +608,7 @@
|
||||
"asset_labels_button": "Generador de Etiquetas",
|
||||
"asset_labels_sub": "Genera un PDF para impresión de etiquetas para un rango de IDs de Activos. Estas etiquetas no son específicas para tu inventario, por lo que puedes imprimirlas con antelación y aplicarlas a tu inventario cuando las recibas.",
|
||||
"bill_of_materials": "Lista de Materiales",
|
||||
"bill_of_materials_button": "Generar Lista de Materiales",
|
||||
"bill_of_materials_button": "Generar lista de materiales",
|
||||
"bill_of_materials_sub": "Genera un archivo CSV (Valores Separados por Comas) que puede importarse a un programa de hojas de cálculo. Es un resumen de tu inventario con información básica sobre artículos y precios."
|
||||
},
|
||||
"reports_sub": "Genera diferentes informes para tu inventario.",
|
||||
|
||||
@@ -24,11 +24,6 @@
|
||||
"new_version_available_link": "Cliquez ici pour consulter les notes de version"
|
||||
}
|
||||
},
|
||||
"color_selector": {
|
||||
"color": "Couleur",
|
||||
"no_color_selected": "Aucune couleur sélectionnée",
|
||||
"randomize": "Couleur aléatoire"
|
||||
},
|
||||
"form": {
|
||||
"password": {
|
||||
"toggle_show": "Activer/désactiver l'affichage du mot de passe"
|
||||
@@ -120,8 +115,8 @@
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Aucun résultat trouvé",
|
||||
"placeholder": "Sélectionner…",
|
||||
"search_placeholder": "Tapez pour rechercher…"
|
||||
"placeholder": "Sélectionner...",
|
||||
"search_placeholder": "Tapez pour rechercher..."
|
||||
},
|
||||
"view": {
|
||||
"selectable": {
|
||||
@@ -134,8 +129,7 @@
|
||||
"headers": "En-têtes",
|
||||
"page": "Page",
|
||||
"rows_per_page": "Lignes par page",
|
||||
"table_settings": "Paramètres du Tableau",
|
||||
"view_item": "Afficher l'élément"
|
||||
"table_settings": "Paramètres du Tableau"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -173,7 +167,7 @@
|
||||
"select_location": "Choisir un emplacement"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "Aucun emplacement disponible. Créez votre premier emplacement avec\nle bouton '<span class=\"link-primary\">'Créer'</span>' dans la barre de navigation."
|
||||
"no_locations": "Aucun emplacement disponible. Créez votre premier emplacement avec\nle bouton `<`span class=\"link-primary\"`>`Créer`<`/span`>` dans la barre de navigation."
|
||||
}
|
||||
},
|
||||
"quick_menu": {
|
||||
@@ -208,7 +202,7 @@
|
||||
"items": "Articles",
|
||||
"join_discord": "Rejoindre le Discord",
|
||||
"labels": "Étiquettes",
|
||||
"loading": "Chargement…",
|
||||
"loading": "Chargement...",
|
||||
"locations": "Emplacements",
|
||||
"maintenance": "Maintenance",
|
||||
"name": "Nom",
|
||||
@@ -406,7 +400,6 @@
|
||||
"th-TH": "Thaï",
|
||||
"tr": "Turc",
|
||||
"uk-UA": "Ukrainien",
|
||||
"vi-VN": "Vietnamien",
|
||||
"zh-CN": "Chinois (simplifié)",
|
||||
"zh-HK": "Chinois (Hong Kong)",
|
||||
"zh-MO": "Chinois (Macao)",
|
||||
@@ -568,10 +561,6 @@
|
||||
"tools": {
|
||||
"actions": "Actions d’inventaire",
|
||||
"actions_set": {
|
||||
"create_missing_thumbnails": "Crée les miniatures manquantes",
|
||||
"create_missing_thumbnails_button": "Crée les miniatures",
|
||||
"create_missing_thumbnails_confirm": "Êtes-vous sûr de vouloir créer les vignettes manquantes ? Cette opération peut prendre un certain temps et ne peut pas être interrompue.",
|
||||
"create_missing_thumbnails_sub": "Crée des miniatures pour toutes les pièces jointes prises en charge par la configuration actuelle. Ceci est utile pour les pièces jointes importées avant la version 0.20.0 de Homebox. Cette opération n'écrase pas les miniatures existantes, mais crée de nouvelles miniatures pour les pièces jointes sans miniature. Veuillez noter que la création des miniatures s'effectue en arrière-plan et peut prendre un certain temps.",
|
||||
"ensure_ids": "Vérifier les ID de ressources",
|
||||
"ensure_ids_button": "Vérifier les ID de ressources",
|
||||
"ensure_ids_confirm": "Êtes-vous certain de vous assurer que toutes les ressources ont une ID ? Cela peut prendre du temps et est irréversible.",
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
{
|
||||
"components": {
|
||||
"app": {
|
||||
"create_modal": {
|
||||
"createAndAddAnother": "Nyomj {shiftKey} + {enterKey}t, hogy létrehozd ezt és hozzáadj egy újat.",
|
||||
"enter": "Enter",
|
||||
"shift": "Shift"
|
||||
},
|
||||
"import_dialog": {
|
||||
"change_warning": "A meglévő import_ref-fel rendelkező tételek importálásának menete megváltozott. Ha a CSV fájlban van import_ref, \nakkor a tételt felülírják a CSV fájlban található értékek.",
|
||||
"description": "Importálj egy CSV fájlt, amely tartalmazza a tételeidet, címkéidet és helyeidet. A szükséges formátumról bővebben \na dokumentációban olvashatsz.",
|
||||
"title": "Importálás CSV-fájlból",
|
||||
"toast": {
|
||||
"import_failed": "Sikertelen importálás. Kérlek próbáld újra később.",
|
||||
"import_success": "Sikeres importálás!",
|
||||
"please_select_file": "Kérlek válassz egy fájlt az importáláshoz."
|
||||
}
|
||||
"title": "Importálás CSV-fájlból"
|
||||
},
|
||||
"outdated": {
|
||||
"current_version": "Jelenlegi verzió",
|
||||
@@ -24,18 +14,6 @@
|
||||
"new_version_available_link": "Kattints ide az újdonságok megtekintéséhez"
|
||||
}
|
||||
},
|
||||
"color_selector": {
|
||||
"clear": "Szín törlése",
|
||||
"color": "Szín",
|
||||
"no_color": "Nincs szín",
|
||||
"no_color_selected": "Nincs kiválasztott szín",
|
||||
"randomize": "Véletlenszerű színezés"
|
||||
},
|
||||
"form": {
|
||||
"password": {
|
||||
"toggle_show": "Jelszó megjelenítése"
|
||||
}
|
||||
},
|
||||
"global": {
|
||||
"copy_text": {
|
||||
"documentation": "dokumentáció",
|
||||
@@ -73,12 +51,7 @@
|
||||
"download": "Címke letöltése",
|
||||
"print": "Címke nyomtatása",
|
||||
"server_print": "Nyomtatás a szerveren",
|
||||
"titles": "Címkék",
|
||||
"toast": {
|
||||
"load_status_failed": "Sikertelen állapot betöltés",
|
||||
"print_failed": "Sikertelen címke nyomtatás",
|
||||
"print_success": "Címke kinyomtatva"
|
||||
}
|
||||
"titles": "Címkék"
|
||||
},
|
||||
"page_qr_code": {
|
||||
"page_url": "Oldal URL-je",
|
||||
@@ -89,52 +62,14 @@
|
||||
}
|
||||
},
|
||||
"item": {
|
||||
"attachments_list": {
|
||||
"download": "Letöltés",
|
||||
"open_new_tab": "Megnyitás új lapon"
|
||||
},
|
||||
"create_modal": {
|
||||
"delete_photo": "Kép törlése",
|
||||
"item_description": "Tétel leírása",
|
||||
"item_name": "Tétel neve",
|
||||
"item_photo": "Tétel fényképe 📷",
|
||||
"item_quantity": "Tételek mennyisége",
|
||||
"parent_item": "Szülő tétel",
|
||||
"product_tooltip_input_barcode": "Automatikus feltöltés kézzel megadott vonalkóddal",
|
||||
"product_tooltip_scan_barcode": "Automatikus kitöltés vonalkóddal innen: 📷",
|
||||
"rotate_photo": "Kép forgatása",
|
||||
"set_as_primary_photo": "Beállítás {isPrimary , select, true {nem } false {} other {}}elsődleges fényképként",
|
||||
"title": "Új elem létrehozása",
|
||||
"toast": {
|
||||
"already_creating": "Elem létrehozása már folyamatban",
|
||||
"create_failed": "Sikertelen elem létrehozás",
|
||||
"create_success": "Elem létrehozva",
|
||||
"failed_load_parent": "Szülő elem betöltése sikertelen - válaszd ki manuálisan",
|
||||
"no_canvas_support": "A böngésződ nem támogatja a canvas műveleteket",
|
||||
"please_select_location": "Válassz egy helyet.",
|
||||
"rotate_failed": "Sikertelen képforgatás: { error }",
|
||||
"rotate_process_failed": "Elforgatott kép feldolgozása sikertelen",
|
||||
"some_photos_failed": "{count, plural, =0 {Nincs feltölthető fénykép.} =1 {1 fénykép feltöltése nem sikerült.} other {Néhány fénykép feltöltése nem sikerült.}}",
|
||||
"upload_failed": "Kép feltöltése sikertelen: { photoName }",
|
||||
"upload_success": "{count, plural, =0 {Nincsenek feltöltött fényképek.} =1 {A fénykép feltöltése sikeres.} other {Minden fénykép feltöltése sikeres.}}",
|
||||
"uploading_photos": "{count, plural, =0 {Nincs feltöltendő fénykép} =1 {1 fénykép feltöltése…} other {{count} fénykép feltöltése…}}"
|
||||
},
|
||||
"upload_photos": "Fotók feltöltése",
|
||||
"uploaded": "Feltöltött fénykép"
|
||||
},
|
||||
"product_import": {
|
||||
"barcode": "Termék vonalkódja",
|
||||
"db_source": "Adatbázis forrás",
|
||||
"error_exception": "Kivétel történt a tétel vonalkódjának lekérése során: ",
|
||||
"error_invalid_barcode": "Érvénytelen vonalkód",
|
||||
"error_not_found": "Nem található termék a megadott vonalkóddal.",
|
||||
"search_item": "Termék keresése",
|
||||
"title": "Termék importálása"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Nincs Találat",
|
||||
"placeholder": "Válassz…",
|
||||
"search_placeholder": "Kezdj gépelni a kereséshez…"
|
||||
"upload_photos": "Fotók feltöltése"
|
||||
},
|
||||
"view": {
|
||||
"selectable": {
|
||||
@@ -147,23 +82,15 @@
|
||||
"headers": "Fejlécek",
|
||||
"page": "Oldal",
|
||||
"rows_per_page": "Sorok oldalanként",
|
||||
"table_settings": "Táblázatbeállítások",
|
||||
"view_item": "Elem megtekintése"
|
||||
"table_settings": "Táblázatbeállítások"
|
||||
}
|
||||
}
|
||||
},
|
||||
"label": {
|
||||
"create_modal": {
|
||||
"label_color": "Címke színe",
|
||||
"label_description": "Címke leírása",
|
||||
"label_name": "Címke neve",
|
||||
"title": "Címke létrehozása",
|
||||
"toast": {
|
||||
"already_creating": "Címke létrehozása már folyamatban",
|
||||
"create_failed": "Sikertelen címke létrehozás",
|
||||
"create_success": "Címke létrehozva",
|
||||
"label_name_too_long": "A címke neve nem lehet hosszabb 50 karakternél"
|
||||
}
|
||||
"title": "Címke létrehozása"
|
||||
},
|
||||
"selector": {
|
||||
"select_labels": "Címkék kiválasztása"
|
||||
@@ -173,12 +100,7 @@
|
||||
"create_modal": {
|
||||
"location_description": "Hely leírása",
|
||||
"location_name": "Hely neve",
|
||||
"title": "Új hely létrehozása",
|
||||
"toast": {
|
||||
"already_creating": "Hely létrehozása már folyamatban",
|
||||
"create_failed": "Sikertelen hely létrehozás",
|
||||
"create_success": "Hely létrehozva"
|
||||
}
|
||||
"title": "Új hely létrehozása"
|
||||
},
|
||||
"selector": {
|
||||
"no_location_found": "Nem található hely",
|
||||
@@ -187,7 +109,7 @@
|
||||
"select_location": "Válassz egy helyet"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "Nincs elérhető hely. Adj hozzá új helyet a\n '<span class=\"link-primary\">'Létrehozás'</span>' gombbal a navigációs sávon."
|
||||
"no_locations": "Nincs elérhető hely. Adj hozzá új helyet a\n `<`span class=\"link-primary\"`>`Létrehozás`<`/span`>` gombbal a navigációs sávon."
|
||||
}
|
||||
},
|
||||
"quick_menu": {
|
||||
@@ -195,9 +117,6 @@
|
||||
"shortcut_hint": "Használd a számgombokat egy művelet gyors kiválasztásához."
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"api_failure": "Backend API hívás sikertelen: "
|
||||
},
|
||||
"global": {
|
||||
"add": "Hozzáadás",
|
||||
"archived": "Archivált",
|
||||
@@ -209,8 +128,6 @@
|
||||
"create_subitem": "Alelem létrehozása",
|
||||
"created": "Létrehozva",
|
||||
"delete": "Törlés",
|
||||
"delete_confirm": "Biztosan törlöd ezt az elemet? ",
|
||||
"demo_instance": "Ez egy demó példány",
|
||||
"details": "Részletek",
|
||||
"duplicate": "Másolás",
|
||||
"edit": "Szerkesztés",
|
||||
@@ -225,7 +142,6 @@
|
||||
"items": "Tételek",
|
||||
"join_discord": "Csatlakozz a Discordhoz",
|
||||
"labels": "Címkék",
|
||||
"loading": "Betöltés…",
|
||||
"locations": "Helyek",
|
||||
"maintenance": "Karbantartás",
|
||||
"name": "Név",
|
||||
@@ -233,14 +149,11 @@
|
||||
"password": "Jelszó",
|
||||
"quantity": "Mennyiség",
|
||||
"read_docs": "Olvasd el a dokumentációt",
|
||||
"return_home": "Vissza a kezdőlapra",
|
||||
"save": "Mentés",
|
||||
"search": "Keresés",
|
||||
"sign_out": "Kijelentkezés",
|
||||
"submit": "Elküldés",
|
||||
"unknown": "Ismeretlen",
|
||||
"update": "Módosítás",
|
||||
"updating": "Frissítés",
|
||||
"value": "Érték",
|
||||
"version": "Verzió: { version }",
|
||||
"welcome": "Üdv, { username }"
|
||||
@@ -265,49 +178,27 @@
|
||||
"set_email": "Mi az email címed?",
|
||||
"set_name": "Mi a neved?",
|
||||
"set_password": "Állíts be egy jelszót!",
|
||||
"tagline": "Kövesd nyomon, rendszerezd és kezeld a dolgaidat.",
|
||||
"title": "Rendezd és címkézd a dolgaidat",
|
||||
"toast": {
|
||||
"invalid_email": "E-mail cím érvénytelen",
|
||||
"invalid_email_password": "Az E-mail vagy jelszó érvénytelen",
|
||||
"login_success": "Sikeres bejelentkezés",
|
||||
"problem_registering": "Hiba a felhasználó regisztrálásakor",
|
||||
"user_registered": "Felhasználó regisztrálva"
|
||||
}
|
||||
"tagline": "Kövesd nyomon, rendszerezd és kezeld a dolgaidat."
|
||||
},
|
||||
"items": {
|
||||
"add": "Hozzáadás",
|
||||
"advanced": "Haladó",
|
||||
"archived": "Archivált",
|
||||
"asset_id": "Eszközazonosító",
|
||||
"associated_with_multiple": "Ez az eszköz Id több elemhez van hozzárendelve",
|
||||
"attachment": "Melléklet",
|
||||
"attachments": "Mellékletek",
|
||||
"changes_persisted_immediately": "A mellékletek módosításai azonnal mentésre kerülnek",
|
||||
"created_at": "Létrehozás dátuma",
|
||||
"custom_fields": "Egyedi mezők",
|
||||
"delete_attachment_confirm": "Biztos törlöd ezt a mellékletet?",
|
||||
"delete_item_confirm": "Biztosan törlöd ezt az elemet?",
|
||||
"description": "Leírás",
|
||||
"details": "Részletek",
|
||||
"drag_and_drop": "Húzd ide a fájlokat, vagy kattints a fájlok kiválasztásához",
|
||||
"edit": {
|
||||
"edit_attachment_dialog": {
|
||||
"attachment_title": "Melléklet Cím",
|
||||
"attachment_type": "Melléklet típusa",
|
||||
"primary_photo": "Elsődleges fénykép",
|
||||
"primary_photo_sub": "Ez a lehetőség csak fényképeknél érhető el. Csak egy fotó lehet elsődleges. Ha kiválasztod ezt a műveletet, a jelenlegi elsődleges fotó elveszti ezt a jellegét.",
|
||||
"select_type": "Válassz típust",
|
||||
"title": "Melléklet szerkesztése"
|
||||
}
|
||||
},
|
||||
"edit_details": "Részletek szerkesztése",
|
||||
"field_selector": "Mezőválasztó",
|
||||
"field_value": "Mező értéke",
|
||||
"first": "Első",
|
||||
"include_archive": "Archivált elemek belefoglalása",
|
||||
"insured": "Biztosítva",
|
||||
"invalid_asset_id": "Érvénytelen eszközazonosító",
|
||||
"last": "Utolsó",
|
||||
"lifetime_warranty": "Élettartam garancia",
|
||||
"location": "Hely",
|
||||
@@ -318,7 +209,6 @@
|
||||
"name": "Név",
|
||||
"negate_labels": "Címkeválasztás negálása",
|
||||
"next_page": "Következő oldal",
|
||||
"no_attachments": "Nem található melléklet",
|
||||
"no_results": "Egy elem sem található",
|
||||
"notes": "Megjegyzések",
|
||||
"only_with_photo": "Csak fényképes tételek",
|
||||
@@ -340,60 +230,24 @@
|
||||
"receipts": "Számlák",
|
||||
"reset_search": "Alaphelyzet",
|
||||
"results": "{total} találat",
|
||||
"select_field": "Válaszd ki a mezőt",
|
||||
"serial_number": "Sorozatszám",
|
||||
"show_advanced_view_options": "További beállítások megjelenítése",
|
||||
"sold_at": "Eladás dátuma",
|
||||
"sold_details": "Eladás részletei",
|
||||
"sold_price": "Eladási ár",
|
||||
"sold_to": "Vevő",
|
||||
"sync_child_locations": "Gyermekelemek helyeinek szinkronizálása",
|
||||
"tip_1": "A hely- és címkeszűrők a „vagy” műveletet használják. Ha egynél többet választasz ki,\n bármelyik egyezése esetén megjelenik a tétel.",
|
||||
"tip_2": "A '#' előtaggal ellátott keresések egy eszközazonosítót fognak lekérdezni (például '#000-001')",
|
||||
"tip_3": "A mezőszűrők a „vagy” műveletet használják. Ha egynél többet választasz ki,\n bármelyik egyezése esetén megjelenik a tétel.",
|
||||
"tips": "Tippek",
|
||||
"tips_sub": "Tippek a kereséshez",
|
||||
"toast": {
|
||||
"asset_not_found": "Az eszköz nem található",
|
||||
"attachment_deleted": "Melléklet törölve",
|
||||
"attachment_updated": "Melléklet frissítve",
|
||||
"attachment_uploaded": "Melléklet feltöltve",
|
||||
"child_items_location_no_longer_synced": "A gyermekelemek helye a továbbiakban nem lesz szinkronizálva ezzel a tétellel.",
|
||||
"child_items_location_synced": "A gyermekelemek helye szinkronizálva lett ezzel a tétellel",
|
||||
"child_location_desync": "A hely módosítása de-szinkronizálja a szülő helyéről",
|
||||
"error_loading_parent_data": "Hiba történt a szülőadatok betöltése során",
|
||||
"failed_adjust_quantity": "Mennyiség beállítása sikertelen",
|
||||
"failed_delete_attachment": "Melléklet törlése sikertelen",
|
||||
"failed_delete_item": "Tétel törlése sikertelen",
|
||||
"failed_duplicate_item": "Tétel másolása sikertelen",
|
||||
"failed_load_asset": "Eszköz betöltése sikertelen",
|
||||
"failed_load_item": "Tétel betöltése sikertelen",
|
||||
"failed_load_items": "Tételek betöltése sikertelen",
|
||||
"failed_save": "Tétel mentése sikertelen",
|
||||
"failed_save_no_location": "Tétel mentése sikertelen: nincs kiválasztott hely",
|
||||
"failed_search_items": "Tételek keresése sikertelen",
|
||||
"failed_update_attachment": "Melléklet frissítése sikertelen",
|
||||
"failed_upload_attachment": "Melléklet feltöltése sikertelen",
|
||||
"item_deleted": "Tétel törölve",
|
||||
"item_saved": "Tétel mentve",
|
||||
"quantity_cannot_negative": "A mennyiség nem lehet negatív",
|
||||
"sync_child_location": "A kiválasztott szülő szinkronizálja gyermekei tartózkodási helyét a sajátjával. Hely frissítve."
|
||||
},
|
||||
"updated_at": "Változtatás dátuma",
|
||||
"warranty": "Garancia",
|
||||
"warranty_details": "Garancia részletei",
|
||||
"warranty_expires": "Garancia vége"
|
||||
},
|
||||
"labels": {
|
||||
"label_delete_confirm": "Biztos vagy benne, hogy törölni szeretnéd ezt a címkét? A művelet nem visszafordítható.",
|
||||
"no_results": "Nem található címke",
|
||||
"toast": {
|
||||
"failed_delete_label": "Címke törlése sikertelen",
|
||||
"failed_load_label": "Címke betöltése sikertelen",
|
||||
"failed_update_label": "Címke frissítése sikertelen",
|
||||
"label_deleted": "Címke törölve",
|
||||
"label_updated": "Címke frissítve"
|
||||
},
|
||||
"update_label": "Címke módosítása"
|
||||
},
|
||||
"languages": {
|
||||
@@ -436,15 +290,7 @@
|
||||
"child_locations": "Tartalmazott helyek",
|
||||
"collapse_tree": "Fanézet becsukása",
|
||||
"expand_tree": "Fa kibontása",
|
||||
"location_items_delete_confirm": "Biztosan törlöd ezt a helyet és az összes elemét? Ez a művelet nem visszavonható.",
|
||||
"no_results": "Nem található hely",
|
||||
"toast": {
|
||||
"failed_delete_location": "Hely törlése sikertelen",
|
||||
"failed_load_location": "Hely betöltése sikertelen",
|
||||
"failed_update_location": "Hely frissítése sikertelen",
|
||||
"location_deleted": "Hely törölve",
|
||||
"location_updated": "Hely frissítve"
|
||||
},
|
||||
"update_location": "Hely módosítása"
|
||||
},
|
||||
"maintenance": {
|
||||
@@ -500,10 +346,7 @@
|
||||
"currency_format": "Pénz formátum",
|
||||
"current_password": "Jelenlegi jelszó",
|
||||
"delete_account": "Fiók törlése",
|
||||
"delete_account_confirm": "Biztosan törlöd a fiókodat? Ha te vagy az utolsó tag a csoportodban, minden adatod törlődik. Ez a művelet nem visszafordítható.",
|
||||
"delete_account_sub": "Törlöd a fiókodat és az összes kapcsolódó adatot. Ezt a műveletet nem lehet visszavonni.",
|
||||
"delete_notifier_confirm": "Biztos, hogy törölni akarod ezt az értesítőt?",
|
||||
"display_legacy_header": "{ currentValue, select, true {Legacy fejléc letiltása} false {Legacy fejléc engedélyezése} other {Nincs találat}}",
|
||||
"enabled": "Engedélyezve",
|
||||
"example": "Példa",
|
||||
"gen_invite": "Meghívó link létrehozása",
|
||||
@@ -521,64 +364,16 @@
|
||||
"test": "Teszt",
|
||||
"theme_settings": "Téma Beállítások",
|
||||
"theme_settings_sub": "A témabeállítások a böngésző helyi tárhelyén tárolódnak. Bármikor megváltoztathatod a témát. Ha problémába\n ütközöl a téma beállításakor, próbáld meg frissíteni az oldalt a böngésződben.",
|
||||
"toast": {
|
||||
"account_deleted": "Fiókodat sikeresen töröltük.",
|
||||
"failed_change_password": "Jelszó megváltoztatása sikertelen.",
|
||||
"failed_create_notifier": "Értesítő létrehozása sikertelen.",
|
||||
"failed_delete_account": "Nem sikerült törölni a fiókodat.",
|
||||
"failed_delete_notifier": "Értesítő törlése sikertelen.",
|
||||
"failed_get_currencies": "Pénznemek lekérése sikertelen",
|
||||
"failed_test_notifier": "Nem sikerült tesztelni az értesítőt.",
|
||||
"failed_update_group": "Csoport frissítése sikertelen",
|
||||
"failed_update_notifier": "Nem sikerült frissíteni az értesítőt.",
|
||||
"group_updated": "Csoport frissítve",
|
||||
"notifier_test_success": "Az értesítő tesztje sikeres volt.",
|
||||
"password_changed": "A jelszóváltoztatás sikeres volt."
|
||||
},
|
||||
"update_group": "Csoport módosítása",
|
||||
"update_language": "Nyelv átállítása",
|
||||
"url": "URL",
|
||||
"user_profile": "Felhasználói profil",
|
||||
"user_profile_sub": "Hívj meg felhasználókat, és kezeld a fiókodat."
|
||||
},
|
||||
"reports": {
|
||||
"label_generator": {
|
||||
"asset_end": "Utolsó eszköz",
|
||||
"asset_start": "Első eszköz",
|
||||
"base_url": "Alap URL",
|
||||
"bordered_labels": "Keretes címkék",
|
||||
"generate_page": "Oldal létrehozása",
|
||||
"input_placeholder": "Írj ide",
|
||||
"instruction_1": "A Homebox Label Generator egy olyan eszköz, amely segít a Homebox-leltár címkéinek nyomtatásában. Ezeket előre\n kinyomtathatod, hogy bármikor felragaszthass egy új, még használatlan címkét",
|
||||
"instruction_2": "Ezek a címkék ezért úgy működnek, hogy egy URL QR-kódot és eszközazonosítót nyomtatnak egy címkére. Ha kikapcsoltad\n az eszközazonosítókat a Homebox beállításokban, akkor is használhatod ezt az eszközt, de nem mutat majd az eszközazonosító semmilyen tételre",
|
||||
"instruction_3": "Ez a funkció korai fejlesztési szakaszban van, és a jövőbeli kiadásokban változhat, ha visszajelzésed van, kérlek\nírd meg nekünk a '<a href=\"https://github.com/sysadminsmedia/homebox/discussions/53\">'GitHub vitafórumon'</a>'",
|
||||
"label_height": "Címke magassága",
|
||||
"label_width": "Címke szélessége",
|
||||
"measure_type": "Mértékegység",
|
||||
"page_bottom_padding": "Oldal alsó margója",
|
||||
"page_height": "Oldal magassága",
|
||||
"page_left_padding": "Oldal bal margója",
|
||||
"page_right_padding": "Oldal jobb margója",
|
||||
"page_top_padding": "Oldal felső margója",
|
||||
"page_width": "Oldalszélesség",
|
||||
"qr_code_example": "QR-kód példa",
|
||||
"tip_1": "Az alapértelmezett beállítások\n'<a href=\"https://www.avery.com/templates/5260\">'Avery 5260 címkeívek'</a>'nek felelnek meg. Ha más íveket használsz,\n módosítanod kell a beállításokat, hogy a laphoz igazodjanak.",
|
||||
"tip_2": "Ha a lapot testre szabod, a méretek hüvelykben vannak megadva. A 5260 ívek létrehozásakor feltűnt,\n hogy a hivatalosan megadott méretek nem azonosak a megfelelő szövegdoboz méretekkel.\n'<b>'Szükséged lehet néhány próbálkozásra a megfelelő méretek megtalálásához.'</b>'",
|
||||
"tip_3": "Nyomtatáskor ügyelj a következőkre:\n '<ol><li>'Állítsd a margókat 0-ra vagy Nincsre'</li><li>'Állítsd a skálázást 100% -ra'</li><li>'Tiltsd le a kétoldalas nyomtatást'</li><li>'Több oldal nyomtatása előtt nyomtass tesztoldalt'</li></ol>'",
|
||||
"tips": "Tippek",
|
||||
"title": "Címkegenerátor",
|
||||
"toast": {
|
||||
"page_too_small_card": "Az oldal mérete túl kicsi a kártya méretéhez képest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
"barcode_detected_message": "termék vonalkód észlelve",
|
||||
"barcode_fetch_data": "Termékadatok lekérése",
|
||||
"error": "Hiba történt a szkennelés közben",
|
||||
"invalid_url": "Érvénytelen vonalkód URL",
|
||||
"no_sources": "Nincs elérhető videóforrás",
|
||||
"permission_denied": "A kamera engedélye megtagadva, engedélyezd a kamerához való hozzáférést a böngésző beállításaiban",
|
||||
"select_video_source": "Videóforrás kiválasztása",
|
||||
"title": "Szkenner",
|
||||
"unsupported": "A Media Stream API nem támogatott HTTPS nélkül"
|
||||
@@ -586,24 +381,17 @@
|
||||
"tools": {
|
||||
"actions": "Készletműveletek",
|
||||
"actions_set": {
|
||||
"create_missing_thumbnails": "Hiányzó bélyegképek létrehozása",
|
||||
"create_missing_thumbnails_button": "Bélyegképek létrehozása",
|
||||
"create_missing_thumbnails_confirm": "Biztosan létrehozod a hiányzó bélyegképeket? Ez eltarthat egy ideig, és nem lehet szüneteltetni.",
|
||||
"create_missing_thumbnails_sub": "Bélyegképeket hoz létre minden olyan melléklethez, melyet a jelenlegi konfiguráció támogat. Ez abban az esetben lehet hasznos, ha a mellékletek a Homebox v0.20.0 verziója előtt lettek feltöltve. A folyamat nem ír felül már létező bélyegképeket, csak újakat készít, ha a melléklet még nem rendelkezik ilyennel. Figyelem, a bélyegképeket egy háttérfolyamat generálja, és egy ideig eltarthat, amíg mind elkészülnek.",
|
||||
"ensure_ids": "Eszközazonosítók meglétének biztosítása",
|
||||
"ensure_ids_button": "Eszközazonosítók generálása",
|
||||
"ensure_ids_confirm": "Biztos elindítod az eszközazonosítók generálását? Ez eltarthat egy ideig, és nem lehet visszavonni.",
|
||||
"ensure_ids_sub": "Biztosítja, hogy a készletben lévő összes tétel rendelkezzen érvényes asset_id (eszközazonosító) mezővel. Ehhez megkeresi a legmagasabb asset_id mezőértéket az adatbázisban és minden olyan tételhez, amelynek nem beállított az asset_id mezője, rendre eggyel növelt értéket állít be. Ezt a created_at mezők értékének (a tétel létrehozásának dátuma) sorrendjében teszi.",
|
||||
"ensure_import_refs": "Importálási hivatkozások meglétének biztosítása",
|
||||
"ensure_import_refs_button": "Hivatkozások generálása",
|
||||
"ensure_import_refs_sub": "Biztosítja, hogy a készletben lévő összes tétel rendelkezzen érvényes import_ref mezővel. Véletlenszerűen generál egy 8 hosszúságú karakterláncot minden olyan tételhez, amelynél az import_ref mező üres.",
|
||||
"set_primary_photo": "Elsődleges fénykép hozzárendelése",
|
||||
"set_primary_photo_button": "Hozzárendelés",
|
||||
"set_primary_photo_confirm": "Biztosan elindítod az elsődleges fényképek hozzárendelését? Ez eltarthat egy ideig, és nem lehet visszavonni.",
|
||||
"set_primary_photo_sub": "A Homebox v0.10.0 verziójában hozzáadtuk a fénykép típusú mellékletekhez az elsődleges fényképként történő megjelölés lehetőségét. Ezzel a művelettel a mellékletekben található első fényképet állítod be elsődleges fényképnek, ha ilyen a tételhez még nincs kiválasztva. '<a class=\"link\" href=\"https://github.com/hay-kot/homebox/pull/576\">'Lásd az #576 GitHub PR-t'</a>'",
|
||||
"zero_datetimes": "Idő törlése a tételek dátummezőiből",
|
||||
"zero_datetimes_button": "Dátummezők javítása",
|
||||
"zero_datetimes_confirm": "Biztosan elindítod az összes dátummező időértékének törlését? Ez eltarthat egy ideig, és nem lehet visszavonni.",
|
||||
"zero_datetimes_sub": "Visszaállítja a dátumot és időt tartalmazó mezők értékét a dátum kezdetére a teljes készletben. Ezzel javíthatsz egy olyan bugot, mely során az oldal fejlesztésének korai szakaszában az időértékek mentése a dátumok pontos megjelenítésében hibát okozott. '<a class=\"link\" href=\"https://github.com/hay-kot/homebox/issues/236\" target=\"_blank\">'Lásd a #236 Github Issue-t további részletekért.'</a>'"
|
||||
},
|
||||
"actions_sub": "Műveletek tömeges alkalmazása a készletre. Ezeket a vissza nem vonható műveleteket csak '<b>'kellő körültekintés mellett használd'</b>'.",
|
||||
@@ -614,7 +402,6 @@
|
||||
"export_sub": "Exportálja a Homebox szabványos CSV formátumát. Ez minden készletedben található tételt exportál.",
|
||||
"import": "Készlet importálása",
|
||||
"import_button": "Készlet importálása",
|
||||
"import_ref_confirm": "Biztos elindítod az importálási hivatkozások meglétének biztosítását? Ez eltarthat egy ideig, és nem lehet visszavonni.",
|
||||
"import_sub": "Importálja a Homebox szabványos CSV formátumát. Amennyiben nem található '<code>'HB.import_ref'</code>' oszlop a fájlban, ez '<b>'nem'</b>' ír felül létező tételeket a készletedben, csak újakat ad hozzá. Azon sorok, melyeknél a '<code>'HB.import_ref'</code>' oszlop értéke megegyezik egy létező tétel import_ref mezőjének értékével, a sor tartalma beolvad a létező tételbe."
|
||||
},
|
||||
"import_export_sub": "Készlet importálása és exportálása CSV-fájlba és CSV-fájlból. Ez hasznos lehet a készleted átmozgatásához a Homebox egy új példányába.",
|
||||
@@ -627,14 +414,6 @@
|
||||
"bill_of_materials_button": "Jegyzék létrehozása",
|
||||
"bill_of_materials_sub": "Létrehoz egy CSV (vesszővel elválasztott értékek) fájlt, amely importálható egy táblázatkezelő programba. Ez a készleted összesítése a tételek alap és árra vonatkozó információival."
|
||||
},
|
||||
"reports_sub": "Hozz létre különböző jelentéseket a készletedhez.",
|
||||
"toast": {
|
||||
"asset_success": "{ results } eszköz frissítve.",
|
||||
"failed_create_missing_thumbnails": "Hiányzó bélyegképek létrehozása sikertelen.",
|
||||
"failed_ensure_ids": "Nem sikerült biztosítani az eszközazonosítók létezését.",
|
||||
"failed_ensure_import_refs": "Importálási hivatkozások meglétének biztosítása sikertelen.",
|
||||
"failed_set_primary_photos": "Nem sikerült beállítani az elsődleges fényképeket.",
|
||||
"failed_zero_datetimes": "Nem sikerült visszaállítani a dátum- és időértékeket."
|
||||
}
|
||||
"reports_sub": "Hozz létre különböző jelentéseket a készletedhez."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,6 @@
|
||||
"new_version_available_link": "Clicca qui per visualizzare le note di rilascio"
|
||||
}
|
||||
},
|
||||
"color_selector": {
|
||||
"clear": "Pulisci il colore",
|
||||
"color": "Colore",
|
||||
"no_color": "Nessun colore",
|
||||
"no_color_selected": "Nessun colore selezionato",
|
||||
"randomize": "Colore casuale"
|
||||
},
|
||||
"form": {
|
||||
"password": {
|
||||
"toggle_show": "Attiva/disattiva visualizzazione password"
|
||||
@@ -100,7 +93,6 @@
|
||||
"item_photo": "Foto dell'articolo 📷",
|
||||
"item_quantity": "Quantità Articoli",
|
||||
"parent_item": "Articolo principale",
|
||||
"product_tooltip_scan_barcode": "Riempimento automatico con un codice a barre da 📷",
|
||||
"rotate_photo": "Ruota foto",
|
||||
"set_as_primary_photo": "Imposta come { isPrimary, select, true {non} false {} other {}} foto principale",
|
||||
"title": "Crea Articolo",
|
||||
@@ -121,17 +113,10 @@
|
||||
"upload_photos": "Carica Foto",
|
||||
"uploaded": "Foto caricata"
|
||||
},
|
||||
"product_import": {
|
||||
"barcode": "Codice a barre del prodotto",
|
||||
"error_exception": "Si è verificato un errore durante il recupero del codice a barre dell'articolo: ",
|
||||
"error_invalid_barcode": "Il codice a barre fornito non è valido",
|
||||
"search_item": "Cerca prodotto",
|
||||
"title": "Importa prodotto"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Nessun risultato trovato",
|
||||
"placeholder": "Seleziona…",
|
||||
"search_placeholder": "Scrivi per cercare…"
|
||||
"placeholder": "Seleziona...",
|
||||
"search_placeholder": "Digita per cercare"
|
||||
},
|
||||
"view": {
|
||||
"selectable": {
|
||||
@@ -183,7 +168,7 @@
|
||||
"select_location": "Seleziona una posizione"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "Nessuna posizione disponibile. Aggiungi nuove posizioni mediante il pulsante\n'<span class=\"link-primary\">'Crea'</span>' nella barra di navigazione."
|
||||
"no_locations": "Nessuna posizione disponibile. Aggiungi nuove posizioni mediante il pulsante\n`<`span class=\"link-primary\"`>`Crea`<`/span`>` nella barra di navigazione."
|
||||
}
|
||||
},
|
||||
"quick_menu": {
|
||||
@@ -218,7 +203,7 @@
|
||||
"items": "Articoli",
|
||||
"join_discord": "Unisciti a Discord",
|
||||
"labels": "Etichette",
|
||||
"loading": "Caricamento…",
|
||||
"loading": "Caricamento...",
|
||||
"locations": "Posizioni",
|
||||
"maintenance": "Manutenzione",
|
||||
"name": "Nome",
|
||||
@@ -295,12 +280,12 @@
|
||||
}
|
||||
},
|
||||
"edit_details": "Modifica dettagli",
|
||||
"field_selector": "Selezione in base ai campi",
|
||||
"field_selector": "Campo Selezione",
|
||||
"field_value": "Campo valore",
|
||||
"first": "Primo",
|
||||
"include_archive": "Includi Articoli Archiviati",
|
||||
"insured": "Assicurato",
|
||||
"invalid_asset_id": "ID dell'asset non valido",
|
||||
"invalid_asset_id": "ID dell'asset non valido.",
|
||||
"last": "Ultimo",
|
||||
"lifetime_warranty": "Garanzia a vita",
|
||||
"location": "Luogo",
|
||||
@@ -326,7 +311,7 @@
|
||||
"purchase_date": "Data di acquisto",
|
||||
"purchase_details": "Dettagli dell'acquisto",
|
||||
"purchase_price": "Prezzo di acquisto",
|
||||
"purchased_from": "Acquistato da",
|
||||
"purchased_from": "Acqistato da",
|
||||
"quantity": "Quantità",
|
||||
"query_id": "ID dell'Asset in Ricerca: { id }",
|
||||
"receipt": "Ricevuta",
|
||||
@@ -336,7 +321,7 @@
|
||||
"select_field": "Seleziona un campo",
|
||||
"serial_number": "Numero seriale",
|
||||
"show_advanced_view_options": "Mostra opzioni di visualizzazione avanzate",
|
||||
"sold_at": "Venduto il",
|
||||
"sold_at": "Venduto su",
|
||||
"sold_details": "Dettagli di vendita",
|
||||
"sold_price": "Prezzo di vendita",
|
||||
"sold_to": "Venduto a",
|
||||
@@ -346,20 +331,16 @@
|
||||
"tip_3": "I filtri di campo utilizzano l'operazione 'OR'. Se ne viene selezionato più di uno, ne sarà\n richiesto solo uno per una corrispondenza.",
|
||||
"tips": "Suggerimenti",
|
||||
"tips_sub": "Suggerimenti per la Ricerca",
|
||||
"toast": {
|
||||
"quantity_cannot_negative": "La quantità non può essere negativa"
|
||||
},
|
||||
"updated_at": "Aggiornato Il",
|
||||
"warranty": "Garanzia",
|
||||
"warranty_details": "Dettagli garanzia",
|
||||
"warranty_expires": "La garanzia scade il"
|
||||
"warranty_expires": "Garanzia scaduta"
|
||||
},
|
||||
"labels": {
|
||||
"no_results": "Nessuna etichetta trovata",
|
||||
"update_label": "Aggiorna etichetta"
|
||||
},
|
||||
"languages": {
|
||||
"bs-BA": "Bosniaco (Bosnia ed Erzegovina)",
|
||||
"ca": "Catalano",
|
||||
"cs-CZ": "Ceco",
|
||||
"de": "Tedesco",
|
||||
@@ -386,7 +367,6 @@
|
||||
"th-TH": "Tailandese",
|
||||
"tr": "Turco",
|
||||
"uk-UA": "Ucraino",
|
||||
"vi-VN": "Vietnamita",
|
||||
"zh-CN": "Cinese (semplificato)",
|
||||
"zh-HK": "Cinese Mandarino",
|
||||
"zh-MO": "Cinese (Macao)",
|
||||
@@ -489,7 +469,7 @@
|
||||
"page_top_padding": "Spaziatura in alto",
|
||||
"page_width": "Larghezza pagina",
|
||||
"qr_code_example": "Esempio di codice QR",
|
||||
"tip_1": "Le impostazioni predefinite qui sono configurate per i\n'<a href=\"https://www.avery.com/templates/5260\">'fogli di etichette ''Avery 5260 '</a>'. Se stai utilizzando un foglio differente,\n devi modificare le impostazioni affinchè corrispondano al tuo foglio."
|
||||
"tip_1": "Le impostazioni predefinite qui sono configurate per i\n\n '<a href=\"https://www.avery.com/templates/5260\">'fogli di etichette ''Avery 5260 '</a>'. Se stai utilizzando un foglio differente,\n\n devi modificare le impostazioni affinchè corrispondano al tuo foglio."
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
|
||||
@@ -1,41 +1,18 @@
|
||||
{
|
||||
"components": {
|
||||
"app": {
|
||||
"create_modal": {
|
||||
"createAndAddAnother": "{shiftKey} + {enterKey}を使用すると、別のアイテムをそのまま追加できます。",
|
||||
"enter": "入る",
|
||||
"shift": "シフト"
|
||||
},
|
||||
"import_dialog": {
|
||||
"change_warning": "【注意】インポート時の動作が変更されました'<br>'\n選択されたCSVファイルにimport_refsの値が存在する場合、該当するアイテムはCSVファイルの値で上書きされます。",
|
||||
"description": "アイテム、ラベル、ロケーション情報を含む CSV ファイルをインポートします。\nデータの形式など詳細については、ドキュメントを参照してください。",
|
||||
"title": ".csvファイルのインポート",
|
||||
"toast": {
|
||||
"import_failed": "読み込みに失敗しました。もう一度お試しください。",
|
||||
"import_success": "インポートに成功しました",
|
||||
"please_select_file": "ファイルを選択してください"
|
||||
}
|
||||
"title": ".csvファイルのインポート"
|
||||
},
|
||||
"outdated": {
|
||||
"current_version": "現在のバージョン",
|
||||
"dismiss": "却下",
|
||||
"latest_version": "最新バージョン",
|
||||
"new_version_available": "利用可能な更新があります",
|
||||
"new_version_available_link": "クリックしてリリースノートを表示"
|
||||
}
|
||||
},
|
||||
"color_selector": {
|
||||
"clear": "色をリセット",
|
||||
"color": "色",
|
||||
"no_color": "色設定なし",
|
||||
"no_color_selected": "色を選択していません",
|
||||
"randomize": "色をランダムに変更する"
|
||||
},
|
||||
"form": {
|
||||
"password": {
|
||||
"toggle_show": "パスワードの表示を切り替え"
|
||||
}
|
||||
},
|
||||
"global": {
|
||||
"copy_text": {
|
||||
"documentation": "ドキュメント",
|
||||
@@ -48,7 +25,6 @@
|
||||
"days": "日",
|
||||
"hour": "時間",
|
||||
"hours": "時間",
|
||||
"in": "{0}内",
|
||||
"just-now": "たった今",
|
||||
"last-month": "先月",
|
||||
"last-week": "先週",
|
||||
@@ -72,69 +48,20 @@
|
||||
"confirm_description": "この管理ラベルを印刷しますか?",
|
||||
"download": "画像として保存",
|
||||
"print": "プリンターで印刷",
|
||||
"server_print": "サーバーで印刷",
|
||||
"titles": "管理ラベルの出力",
|
||||
"toast": {
|
||||
"load_status_failed": "ステータスの読み込みに失敗しました",
|
||||
"print_failed": "ラベルの印刷に失敗しました",
|
||||
"print_success": "ラベルを印刷しました"
|
||||
}
|
||||
"titles": "管理ラベルの出力"
|
||||
},
|
||||
"page_qr_code": {
|
||||
"page_url": "ページ URL",
|
||||
"qr_tooltip": "QRコードを表示"
|
||||
"page_url": "ページ URL"
|
||||
},
|
||||
"password_score": {
|
||||
"password_strength": "パスワード強度"
|
||||
}
|
||||
},
|
||||
"item": {
|
||||
"attachments_list": {
|
||||
"download": "ダウンロード",
|
||||
"open_new_tab": "新しいタブで開く"
|
||||
},
|
||||
"create_modal": {
|
||||
"delete_photo": "写真を削除",
|
||||
"item_description": "説明",
|
||||
"item_name": "名称",
|
||||
"item_photo": "商品写真 📷",
|
||||
"item_quantity": "数量",
|
||||
"parent_item": "関連アイテム",
|
||||
"product_tooltip_input_barcode": "バーコードを手動入力して自動取得を試みる",
|
||||
"product_tooltip_scan_barcode": "バーコードを撮影して自動取得を試みる",
|
||||
"rotate_photo": "写真を回転",
|
||||
"set_as_primary_photo": "{isPrimary, select, true {non} false {} other {}} サムネイルに設定",
|
||||
"title": "アイテム情報の追加",
|
||||
"toast": {
|
||||
"already_creating": "既に同じアイテムがあります",
|
||||
"create_failed": "アイテムを作成できませんでした",
|
||||
"create_success": "アイテムを作成しました",
|
||||
"failed_load_parent": "親アイテムの読み込みに失敗しました。手動で選択してください",
|
||||
"no_canvas_support": "このブラウザはHTML5 canvasをサポートしていません",
|
||||
"please_select_location": "ロケーションを選択",
|
||||
"rotate_failed": "画像の回転ができませんでした。エラー内容: { error }",
|
||||
"rotate_process_failed": "画像回転の処理でエラーが発生しました",
|
||||
"some_photos_failed": "{count, plural, = 0 {アップロード可能な画像がありません} = 1 {画像のアップロードに失敗しました} other {一部の画像のアップロードに失敗しました}}",
|
||||
"upload_failed": "画像のアップロードに失敗しました ({ photoName })",
|
||||
"upload_success": "{count, plural, = 0 {画像はアップロードされていません} = 1 {画像をアップロードしました} other {すべての画像をアップロードしました}}",
|
||||
"uploading_photos": "{count, plural, = 0 {アップロード可能な画像がありません} = 1 {画像をアップロードしています} other {{count}枚の画像をアップロードしています…}}"
|
||||
},
|
||||
"upload_photos": "画像をアップロード",
|
||||
"uploaded": "アップロードした画像"
|
||||
},
|
||||
"product_import": {
|
||||
"barcode": "商品のバーコード",
|
||||
"db_source": "情報提供元",
|
||||
"error_exception": "バーコード情報を取得できませんでした ",
|
||||
"error_invalid_barcode": "このバーコードは使えません",
|
||||
"error_not_found": "入力されたバーコードの製品が見つかりません。手動登録が必要です。",
|
||||
"search_item": "商品を検索",
|
||||
"title": "商品をインポート"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "一致するものがありません",
|
||||
"placeholder": "選択してください…",
|
||||
"search_placeholder": "入力してください"
|
||||
"title": "アイテム情報の追加"
|
||||
},
|
||||
"view": {
|
||||
"selectable": {
|
||||
@@ -144,103 +71,62 @@
|
||||
"table": "テーブル"
|
||||
},
|
||||
"table": {
|
||||
"headers": "ヘッダー",
|
||||
"page": "ページ",
|
||||
"rows_per_page": "表示件数",
|
||||
"table_settings": "テーブル表示の設定",
|
||||
"view_item": "アイテムを見る"
|
||||
"rows_per_page": "表示件数"
|
||||
}
|
||||
}
|
||||
},
|
||||
"label": {
|
||||
"create_modal": {
|
||||
"label_color": "ラベルの色",
|
||||
"label_description": "ラベルの説明",
|
||||
"label_name": "ラベル名",
|
||||
"title": "ラベルの追加",
|
||||
"toast": {
|
||||
"already_creating": "既に同じラベルがあります",
|
||||
"create_failed": "ラベルを作成できませんでした",
|
||||
"create_success": "ラベルを作成しました",
|
||||
"label_name_too_long": "ラベル名は50文字以内で入力してください"
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"select_labels": "ラベルを選択"
|
||||
"title": "ラベルの追加"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"create_modal": {
|
||||
"location_description": "場所の詳細",
|
||||
"location_name": "名称",
|
||||
"title": "ロケーション(場所)の追加",
|
||||
"toast": {
|
||||
"already_creating": "既に同じロケーションがあります",
|
||||
"create_failed": "ロケーションを作成できませんでした",
|
||||
"create_success": "ロケーションを作成しました"
|
||||
}
|
||||
"title": "ロケーション(場所)の追加"
|
||||
},
|
||||
"selector": {
|
||||
"no_location_found": "一致するロケーションがありません",
|
||||
"parent_location": "親項目 (選択された項目の下位にネスト)",
|
||||
"search_location": "ロケーションを検索",
|
||||
"select_location": "ロケーションを選択"
|
||||
"parent_location": "親項目 (選択された項目の下位にネスト)"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "ロケーションが設定されていません。\n左上の '<span class=\"link-primary\">'項目の追加'</span>' ボタンでロケーションを追加してください。"
|
||||
"no_locations": "場所の項目は存在しません。\nナビゲーションバー上部の\"Create\"から追加してください。"
|
||||
}
|
||||
},
|
||||
"quick_menu": {
|
||||
"no_results": "一致する項目はありません",
|
||||
"shortcut_hint": "数字キーを入力することで対応する操作を実行できます"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"api_failure": "サーバーとの通信に失敗しました "
|
||||
},
|
||||
"global": {
|
||||
"add": "追加",
|
||||
"archived": "アーカイブ済み",
|
||||
"build": "ビルド番号: { build }",
|
||||
"cancel": "キャンセル",
|
||||
"confirm": "確認が必要です",
|
||||
"create": "項目の追加",
|
||||
"create": "追加する",
|
||||
"create_and_add": "続けて追加できます",
|
||||
"create_subitem": "サブアイテムを作成",
|
||||
"created": "作成済み",
|
||||
"delete": "削除",
|
||||
"delete_confirm": "このアイテムを削除しますか? ",
|
||||
"demo_instance": "これはデモ環境です。データは新しいバージョンになると初期化されます。",
|
||||
"details": "製品の情報",
|
||||
"duplicate": "複製",
|
||||
"edit": "編集",
|
||||
"email": "メール",
|
||||
"follow_dev": "開発者をフォローする",
|
||||
"footer": {
|
||||
"api_link": "'<a href=\"https://homebox.software/en/api/\" target=\"_blank\">'APIリファレンス(英語)'</a>'",
|
||||
"version_link": "'<'a href=\"https://github.com/sysadminsmedia/homebox/releases/tag/{ version }\" target=\"_blank\"'>' Version: { version } Build: { build } '</a>'"
|
||||
},
|
||||
"github": "GitHub プロジェクト",
|
||||
"insured": "保険適用",
|
||||
"items": "アイテム",
|
||||
"join_discord": "Discordサーバーへの参加",
|
||||
"labels": "ラベル",
|
||||
"loading": "読込中…",
|
||||
"locations": "ロケーション",
|
||||
"maintenance": "メンテナンス情報",
|
||||
"name": "名前",
|
||||
"navigate": "移動",
|
||||
"password": "パスワード",
|
||||
"quantity": "数量",
|
||||
"read_docs": "ドキュメントを読む",
|
||||
"return_home": "トップページに戻る",
|
||||
"save": "保存",
|
||||
"search": "検索",
|
||||
"sign_out": "ログアウト",
|
||||
"submit": "送信",
|
||||
"unknown": "不明",
|
||||
"update": "アップデート",
|
||||
"updating": "更新中",
|
||||
"value": "値",
|
||||
"version": "現在のバージョン: { version }",
|
||||
"welcome": "{ username } でログイン中"
|
||||
@@ -261,53 +147,31 @@
|
||||
"joining_group": "既存のグループの招待を受け取りました!",
|
||||
"login": "ログイン",
|
||||
"register": "新規登録",
|
||||
"remember_me": "ログインしたままにする",
|
||||
"remember_me": "ログインを維持する",
|
||||
"set_email": "メールアドレスを入力してください",
|
||||
"set_name": "お名前は何ですか?",
|
||||
"set_password": "パスワードを入力してください",
|
||||
"tagline": "Track, Organize, and Manage your Things.",
|
||||
"title": "Organize and Tag Your Stuff",
|
||||
"toast": {
|
||||
"invalid_email": "無効なメールアドレス",
|
||||
"invalid_email_password": "無効なメールアドレスまたはパスワードです",
|
||||
"login_success": "ログインしました",
|
||||
"problem_registering": "ユーザー情報を登録できませんでした",
|
||||
"user_registered": "ユーザーを登録しました"
|
||||
}
|
||||
"tagline": "Track, Organize, and Manage your Things."
|
||||
},
|
||||
"items": {
|
||||
"add": "項目を追加",
|
||||
"advanced": "詳細項目の表示",
|
||||
"archived": "アーカイブ済み",
|
||||
"asset_id": "Asset ID",
|
||||
"associated_with_multiple": "このAsset IDは重複しています",
|
||||
"attachment": "添付ファイル",
|
||||
"attachments": "添付ファイル",
|
||||
"changes_persisted_immediately": "添付ファイルの保存は自動で行われます",
|
||||
"created_at": "作成日",
|
||||
"custom_fields": "カスタム項目",
|
||||
"delete_attachment_confirm": "この添付ファイルを削除しますか?",
|
||||
"delete_item_confirm": "このアイテムを削除しますか?",
|
||||
"description": "説明",
|
||||
"details": "この製品の情報",
|
||||
"drag_and_drop": "添付ファイルはドラッグ&ドロップ、もしくはクリックで追加できます",
|
||||
"edit": {
|
||||
"edit_attachment_dialog": {
|
||||
"attachment_title": "添付ファイルのタイトル",
|
||||
"attachment_type": "添付ファイルの種類",
|
||||
"primary_photo": "サムネイルに設定",
|
||||
"primary_photo_sub": "この画像をアイテムサムネイルに設定します。既に画像が設定されている場合は、このチェックボックスを有効にすると設定が上書きされます。",
|
||||
"select_type": "種類を選択する",
|
||||
"title": "添付ファイルの編集"
|
||||
}
|
||||
},
|
||||
"edit_details": "基本的な情報の編集",
|
||||
"field_selector": "カスタム値で絞り込む",
|
||||
"field_value": "フィールド値",
|
||||
"first": "最初の",
|
||||
"include_archive": "「現在は使用していない」にチェックを入れた項目を検索に含める",
|
||||
"insured": "保険適用",
|
||||
"invalid_asset_id": "このAsset IDは無効です",
|
||||
"last": "最後",
|
||||
"lifetime_warranty": "無期限保証",
|
||||
"location": "場所",
|
||||
@@ -318,7 +182,6 @@
|
||||
"name": "名称",
|
||||
"negate_labels": "選択されたラベルを除外",
|
||||
"next_page": "次の ページ",
|
||||
"no_attachments": "添付ファイルはありません",
|
||||
"no_results": "一致する項目はありません",
|
||||
"notes": "備考",
|
||||
"only_with_photo": "写真付きのアイテムのみ",
|
||||
@@ -340,64 +203,27 @@
|
||||
"receipts": "レシート(領収書)",
|
||||
"reset_search": "検索条件をクリア",
|
||||
"results": "検索結果: { total } 件",
|
||||
"select_field": "フィールドを選択",
|
||||
"serial_number": "シリアル番号(S/N)",
|
||||
"show_advanced_view_options": "高度なオプションを表示",
|
||||
"sold_at": "売却日",
|
||||
"sold_details": "売却時の情報",
|
||||
"sold_price": "売却価格",
|
||||
"sold_to": "売却先",
|
||||
"sync_child_locations": "ペア設定されたアイテムの場所も同時に変更する",
|
||||
"tip_1": "フィルター条件を複数選択した場合、\nいずれかに該当している項目すべてをフィルター条件に一致しているとみなします。",
|
||||
"tip_2": "Asset IDで検索する場合は「#」を先頭に入力してください (例: '#000-001')",
|
||||
"tip_3": "カスタム値絞り込み(Field Selector)は、\n複数選択された場合にどれかが該当するアイテムすべてを一致しているとみなしします。",
|
||||
"tips": "ヒント",
|
||||
"tips_sub": "検索に関するヒント",
|
||||
"toast": {
|
||||
"asset_not_found": "アイテムがありません",
|
||||
"attachment_deleted": "添付ファイルを削除しました",
|
||||
"attachment_updated": "添付ファイルを更新",
|
||||
"attachment_uploaded": "添付ファイルをアップロードしました",
|
||||
"child_items_location_no_longer_synced": "子アイテムのロケーションはこのアイテムと同期されなくなります。",
|
||||
"child_items_location_synced": "子アイテムのロケーションはこのアイテムと同期されています",
|
||||
"child_location_desync": "ロケーションを変更すると、親アイテムのロケーション同期が解除されます",
|
||||
"error_loading_parent_data": "親アイテムの読み込み中にエラーが発生しました",
|
||||
"failed_adjust_quantity": "数量の変更に失敗しました",
|
||||
"failed_delete_attachment": "添付ファイルの削除に失敗しました",
|
||||
"failed_delete_item": "アイテムの削除に失敗しました",
|
||||
"failed_duplicate_item": "アイテムの複製に失敗しました",
|
||||
"failed_load_asset": "アセットの読み込みに失敗しました",
|
||||
"failed_load_item": "アイテムの読み込みに失敗しました",
|
||||
"failed_load_items": "アイテムの読み込みに失敗しました",
|
||||
"failed_save": "アイテム設定の保存に失敗しました",
|
||||
"failed_save_no_location": "アイテム設定の保存に失敗しました: ロケーションを選択してください",
|
||||
"failed_search_items": "アイテム検索に失敗しました",
|
||||
"failed_update_attachment": "添付ファイルの更新に失敗しました",
|
||||
"failed_upload_attachment": "添付ファイルのアップロードに失敗しました",
|
||||
"item_deleted": "アイテムを削除しました",
|
||||
"item_saved": "アイテム設定を保存しました",
|
||||
"quantity_cannot_negative": "数量はマイナスにできません",
|
||||
"sync_child_location": "ペア設定されたアイテムのロケーションも更新されました。"
|
||||
},
|
||||
"updated_at": "更新日",
|
||||
"warranty": "保証書",
|
||||
"warranty_details": "保証に関する情報",
|
||||
"warranty_expires": "保証期間"
|
||||
},
|
||||
"labels": {
|
||||
"label_delete_confirm": "このラベルを削除しますか?元に戻すことはできません。",
|
||||
"no_results": "一致するラベルはありません",
|
||||
"toast": {
|
||||
"failed_delete_label": "ラベルの削除に失敗しました",
|
||||
"failed_load_label": "ラベルの読み込みに失敗しました",
|
||||
"failed_update_label": "ラベルの更新に失敗しました",
|
||||
"label_deleted": "ラベルが削除されました",
|
||||
"label_updated": "ラベル設定が更新されました"
|
||||
},
|
||||
"update_label": "ラベル設定の変更"
|
||||
},
|
||||
"languages": {
|
||||
"bs-BA": "ボスニア語 (Bosnia and Herzegovina)",
|
||||
"ca": "カタルーニャ語 (カタロニア語)",
|
||||
"cs-CZ": "チェコ語",
|
||||
"de": "ドイツ語",
|
||||
@@ -410,9 +236,6 @@
|
||||
"it": "イタリア語",
|
||||
"ja-JP": "日本語",
|
||||
"ko-KR": "韓国語",
|
||||
"lb-LU": "ルクセンブルク語 (Luxembourg)",
|
||||
"lt-LT": "リトアニア語 (Lithuania)",
|
||||
"nb-NO": "ノルウェー語 (ブークモール)",
|
||||
"nl": "オランダ語",
|
||||
"pl": "ポーランド語",
|
||||
"pt-BR": "ポルトガル語 (ブラジル)",
|
||||
@@ -424,7 +247,6 @@
|
||||
"th-TH": "タイ語",
|
||||
"tr": "トルコ語",
|
||||
"uk-UA": "ウクライナ語",
|
||||
"vi-VN": "ベトナム語",
|
||||
"zh-CN": "中国語 (簡体字)",
|
||||
"zh-HK": "中国語 (香港)",
|
||||
"zh-MO": "中国語 (マカオ)",
|
||||
@@ -437,16 +259,7 @@
|
||||
"locations": {
|
||||
"child_locations": "属しているその他のロケーション",
|
||||
"collapse_tree": "ツリーを折りたたむ",
|
||||
"expand_tree": "ツリーを展開",
|
||||
"location_items_delete_confirm": "このロケーションと、紐づいているアイテムをすべて削除します。'<br>'このロケーションに紐づけられたアイテムはすべて削除されます。'<br>'元に戻すことはできません。",
|
||||
"no_results": "ロケーションはありません",
|
||||
"toast": {
|
||||
"failed_delete_location": "ロケーションの削除に失敗しました",
|
||||
"failed_load_location": "ロケーションの読み込みに失敗しました",
|
||||
"failed_update_location": "ロケーション設定の更新に失敗しました",
|
||||
"location_deleted": "ロケーションを削除しました",
|
||||
"location_updated": "ロケーション設定を更新しました"
|
||||
},
|
||||
"no_results": "指定された場所は見つかりません",
|
||||
"update_location": "場所情報の変更"
|
||||
},
|
||||
"maintenance": {
|
||||
@@ -502,10 +315,7 @@
|
||||
"currency_format": "通貨の種類",
|
||||
"current_password": "現在のパスワード",
|
||||
"delete_account": "アカウントの削除 (永久的です!)",
|
||||
"delete_account_confirm": "アカウントを削除しますか?あなたがこのグループのメンバーの最後の一人の場合、アイテム・ロケーション・ラベルなどすべてのデータが削除されます。'<br>'【最終確認】'<u>'この削除機能に再確認ポップアップはありません。続行した場合、すぐにデータが削除され復元もできません。'<u>'",
|
||||
"delete_account_sub": "アカウントと関連するデータをすべて削除します。元に戻すことはできません。",
|
||||
"delete_notifier_confirm": "この通知設定を削除してもよろしいですか?",
|
||||
"display_legacy_header": "{currentValue, select, true {レガシーヘッダーを無効にする} false {レガシーヘッダーを有効にする} other {ヒットしない}}",
|
||||
"enabled": "有効",
|
||||
"example": "例",
|
||||
"gen_invite": "招待リンクの作成",
|
||||
@@ -515,97 +325,36 @@
|
||||
"language": "言語",
|
||||
"new_password": "新しいパスワード",
|
||||
"no_notifiers": "通知機能は設定されていません",
|
||||
"no_override": "既定の言語",
|
||||
"notifier_modal": "{type, select, true {編集} false {作成} other {その他}}通知者",
|
||||
"notifiers": "通知",
|
||||
"notifiers_sub": "メンテナンスなどのリマインダー通知を受け取れます",
|
||||
"override_locale": "日時・通貨の言語を設定 (上記の言語設定とは別の言語を設定可能)",
|
||||
"test": "テスト",
|
||||
"theme_settings": "テーマ設定",
|
||||
"theme_settings_sub": "テーマ設定はブラウザに保存されます。いつでも変更できます。\nテーマ設定によって問題が発生した場合は、再読み込みを行ってください。",
|
||||
"toast": {
|
||||
"account_deleted": "アカウントを削除しました",
|
||||
"failed_change_password": "パスワードを変更できませんでした",
|
||||
"failed_create_notifier": "通知設定の作成に失敗しました。",
|
||||
"failed_delete_account": "アカウントの削除に失敗しました",
|
||||
"failed_delete_notifier": "通知設定の削除に失敗しました",
|
||||
"failed_get_currencies": "通貨情報の取得に失敗しました",
|
||||
"failed_test_notifier": "通知の送信テストに失敗しました",
|
||||
"failed_update_group": "グループ設定の更新に失敗しました",
|
||||
"failed_update_notifier": "通知設定の更新に失敗しました",
|
||||
"group_updated": "グループ設定を更新しました",
|
||||
"notifier_test_success": "通知の送信テストに成功しました",
|
||||
"password_changed": "パスワードを変更しました"
|
||||
},
|
||||
"update_group": "グループ設定を更新",
|
||||
"update_language": "言語更新",
|
||||
"url": "URL",
|
||||
"user_profile": "ユーザー情報",
|
||||
"user_profile_sub": "アカウントの管理やユーザーの招待など"
|
||||
},
|
||||
"reports": {
|
||||
"label_generator": {
|
||||
"asset_end": "Asset ID 印刷終了位置 (このIDは含みません)",
|
||||
"asset_start": "Asset ID 印刷開始位置 (このIDを含みます)",
|
||||
"base_url": "QRコードのURL (通常変更する必要はありません。URL見本を参照し設定してください。)",
|
||||
"bordered_labels": "ラベルの境界線を印刷",
|
||||
"generate_page": "印刷プレビューを取得",
|
||||
"input_placeholder": "ここに入力",
|
||||
"instruction_1": "Homebox ラベルジェネレーターは、Homeboxに登録されたアイテムの管理シールを印刷するツールです。\nシールはアイテムを登録する前に印刷できるため、事前に大量作成することが可能です!",
|
||||
"instruction_2": "シールには、HomeboxのQRコードやAsset IDを印刷できます。\nAsset IDを無効にしている場合でもこのツールは使用できますが、Asset IDを利用してアイテムページを開く機能などは利用できません。",
|
||||
"instruction_3": "この機能はまだ開発中であり、今後の更新で改修される可能性があります。\nバグ情報や機能提案がある方は、ぜひ'<a href=\"https://github.com/sysadminsmedia/homebox/discussions/53\">'GitHubディスカッションに投稿をお願いします。'</a>'",
|
||||
"label_height": "シール 高さ",
|
||||
"label_width": "シール 横幅",
|
||||
"measure_type": "メジャータイプ",
|
||||
"page_bottom_padding": "用紙 下余白",
|
||||
"page_height": "用紙サイズ 高さ",
|
||||
"page_left_padding": "用紙 左余白",
|
||||
"page_right_padding": "用紙 右余白",
|
||||
"page_top_padding": "用紙 上余白",
|
||||
"page_width": "用紙サイズ 横幅",
|
||||
"qr_code_example": "QRコード URL見本",
|
||||
"tip_1": "この機能の既定値は、'<a href=\"https://www.avery.com/templates/5260\">'Avery 5260 label sheets'</a>'と適合する値になっています。\nそれ以外のシートを使用する場合は、必ず設定を調整する必要があります。",
|
||||
"tip_2": "シールをカスタマイズする場合、寸法はインチ単位で設定してください。\n5260シートも、既定値が寸法と一致しないことがあります。\n'<br>' '<b>'試行錯誤が必要になることを覚悟してください。'<u>'必ず寸法や印刷位置の検証を行ってから、シール用紙に印刷してください。'</u>' '</b>'",
|
||||
"tip_3": "印刷時は、以下のことに気を付けてください\n'<ol><li>'余白を0 または なし に設定する\n'</li><li>'拡大率を100%に設定する\n'</li><li>'両面印刷を無効にする\n'</li><li>'ページを印刷する前に必ずテスト印刷をする\n'</li><li>'たくさんの紙くずが発生することを覚悟する",
|
||||
"tips": "仕様",
|
||||
"title": "管理シール印刷機能",
|
||||
"toast": {
|
||||
"page_too_small_card": "印刷可能範囲がシールサイズより小さいため印刷プレビューを生成できません。用紙サイズ・シールサイズ・余白を変更してください。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
"barcode_detected_message": "バーコードが見つかりました",
|
||||
"barcode_fetch_data": "製品のデータを取得",
|
||||
"error": "不明なエラーが発生しました。スキャンは利用できません。",
|
||||
"invalid_url": "バーコードが無効です",
|
||||
"no_sources": "映像デバイスが見つかりません",
|
||||
"permission_denied": "カメラアクセスが拒否されています。ブラウザ設定・スマホ設定で許可してください。",
|
||||
"select_video_source": "映像デバイスを選択",
|
||||
"title": "スキャン",
|
||||
"unsupported": "メディアストリームAPIはHTTPSのみをサポートしています"
|
||||
},
|
||||
"tools": {
|
||||
"actions": "全てのアイテムに対する変更",
|
||||
"actions_set": {
|
||||
"create_missing_thumbnails": "サムネイルを自動設定",
|
||||
"create_missing_thumbnails_button": "サムネイルを作成",
|
||||
"create_missing_thumbnails_confirm": "サムネイルを作成してもよろしいですか?件数によっては時間がかかる場合があり、一時停止することはできません。既存のサムネイルは上書きされません。元に戻すことはできません。",
|
||||
"create_missing_thumbnails_sub": "アップロードされた添付ファイルのサムネイルを自動で作成します。既にサムネイルがある場合はスキップされます。サムネイルが存在しない場合に、自動で作成します。性能やファイル数により、処理が完了するまでに時間がかかる場合があります。",
|
||||
"ensure_ids": "アイテムのAsset IDの確認",
|
||||
"ensure_ids_button": "確認の実行",
|
||||
"ensure_ids_confirm": "すべてのアイテムにAsset IDがあることを確認します。この処理には時間がかかる場合があり、この処理によってAsset IDが変更されても元に戻すことはできません。",
|
||||
"ensure_ids_sub": "全てのアイテムに、有効なAsset IDが存在することを確認します。'<br>'もし存在しないアイテムがあった場合は、登録された日付が古い順にAsset IDの登録を行います。",
|
||||
"ensure_import_refs": "import_refsの確認",
|
||||
"ensure_import_refs_button": "確認する",
|
||||
"ensure_import_refs_sub": "全てのアイテムに、有効な import_ref の値 が設定されていることを確認します。'<br>' 設定されていない場合は、ランダムな8文字の文字列を設定します。'<br>'import_refは内部管理用のIDで、ユーザーは見ることができません。",
|
||||
"set_primary_photo": "アイテムサムネイルの自動設定",
|
||||
"ensure_import_refs": "Import Refsの確認",
|
||||
"ensure_import_refs_button": "確認の実行",
|
||||
"ensure_import_refs_sub": "全てのアイテムに、有効な import_refに対応する値 が設定されていることを確認します。'<br>' 設定されていない場合は、ランダムな8文字の文字列を設定します。",
|
||||
"set_primary_photo": "アイテムの見出し画像の自動設定",
|
||||
"set_primary_photo_button": "操作を実行",
|
||||
"set_primary_photo_confirm": "サムネイルを自動設定します。'<br>'件数によっては時間がかかる場合があり、元に戻すことはできません。",
|
||||
"set_primary_photo_sub": "Homebox v0.10.0 (2023年10月10日リリース)にて、アイテムのサムネイル画像を設定できるようになりました。'<br>'サムネイルが設定されていないアイテムは、この機能を利用して自動で設定できます。'<br>'この機能は、サムネイルが未設定のアイテムの添付画像の中で最も古い画像ファイルを自動的にサムネイルとして設定します。'<br>'詳しくは、'<a class=\"link\" target=\"_brank\" href=\"https://github.com/hay-kot/homebox/pull/576\">'このページをご覧ください (GitHub PR #576)'</a>'",
|
||||
"set_primary_photo_sub": "Homebox v0.10.0にて、画像の添付ファイルのうち1枚を見出し画像(Primary Photo)として設定できるようになりました。'<br>'未設定のアイテムは、この機能を利用して自動で設定できます。'<br>'この機能は、見出し画像が未設定のアイテムと関連付けられている中で最も古い画像ファイルを自動的にアイテムの見出し画像として設定します。'<br>'詳しくは、'<a class=\"link\" href=\"https://github.com/hay-kot/homebox/pull/576\">'このページをご覧ください (GitHub PR #576)'</a>'",
|
||||
"zero_datetimes": "アイテムに設定された日時を消去",
|
||||
"zero_datetimes_button": "操作を実行",
|
||||
"zero_datetimes_confirm": "すべてアイテムの日付と時刻の値をリセットします。'<br>'件数によっては時間がかかる場合があり、元に戻すことはできません。",
|
||||
"zero_datetimes_sub": "全てのアイテムの'<b>'日付をリセット'</b>'します。'<br>'これは、初期(v0.8.0 / 2023-02-18以前)に発生したバグの修正に必要です。日付が適切に表示されなくなるといった問題が発生している場合に限り、実行してください。'<br>'バグが発生していない場合は実行は不要です。'<a class=\"link\" href=\"https://github.com/hay-kot/homebox/issues/236\" target=\"_blank\">'詳しくはこちらをご覧ください (GitHub Issue #236)'</a>'"
|
||||
},
|
||||
"actions_sub": "以下の機能は、すべてのアイテムのデータに影響を与えます。'<b>'これらの操作を元に戻すことはできません。'</b>'",
|
||||
@@ -616,7 +365,6 @@
|
||||
"export_sub": "Homeboxで利用可能なCSV形式のファイルをエクスポートします。'<br>'すべてのアイテムが対象です。一部のアイテムを選択してエクスポートすることはできません。",
|
||||
"import": "インポート (CSVの取り込み)",
|
||||
"import_button": "CSVファイルを選択",
|
||||
"import_ref_confirm": "すべてのアイテムに import_ref の値があることを確認します。'<br>'件数によっては時間がかかる場合があり、元に戻すことはできません。",
|
||||
"import_sub": "Homeboxで利用可能なCSVファイルをインポートします。\n'<code>'HB.import_ref'</code>'が存在しないアイテムに重複がある場合は上書きされません。\n'<code>'HB.import_ref'</code>'が存在するアイテムに重複がある場合は、同じ'<code>'HB.import_ref'</code>'を持つアイテムのデータが上書きされます。\nどちらも、重複していない場合は関係なく追加されます。"
|
||||
},
|
||||
"import_export_sub": "登録されたアイテムをCSVファイルにインポートおよびエクスポートします。Homeboxのソフトウェアを切り替える場合などに便利です。",
|
||||
@@ -629,14 +377,6 @@
|
||||
"bill_of_materials_button": "部品表(BOM)を生成する",
|
||||
"bill_of_materials_sub": "CSV形式のファイルを出力します。基本的なアイテム情報と価格情報を含むアイテムの概要が含まれています。'<br>'UTF-8形式で出力されるため、Microsoft OfficeのExcelで開くと文字化けする恐れがあります。"
|
||||
},
|
||||
"reports_sub": "アイテムに関するデータの含まれたファイルを作成できます。",
|
||||
"toast": {
|
||||
"asset_success": "{results}件のアイテムが更新されました。",
|
||||
"failed_create_missing_thumbnails": "サムネイルの自動作成ができませんでした。",
|
||||
"failed_ensure_ids": "Asset IDの確認に失敗しました。",
|
||||
"failed_ensure_import_refs": "import_ref の確認に失敗しました。",
|
||||
"failed_set_primary_photos": "サムネイルの設定に失敗しました。",
|
||||
"failed_zero_datetimes": "日時のリセットに失敗しました。"
|
||||
}
|
||||
"reports_sub": "アイテムに関するデータの含まれたファイルを作成できます。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
{
|
||||
"components": {
|
||||
"app": {
|
||||
"create_modal": {
|
||||
"enter": "Enter",
|
||||
"shift": "Sjoft"
|
||||
},
|
||||
"import_dialog": {
|
||||
"title": "CSV 파일 들여오기",
|
||||
"toast": {
|
||||
@@ -16,7 +12,6 @@
|
||||
"outdated": {
|
||||
"current_version": "현재 버전",
|
||||
"latest_version": "최신 버전",
|
||||
"new_version_available": "새 버전이 있습니다",
|
||||
"new_version_available_link": "릴리스 노트를 보려면 여기를 클릭하세요"
|
||||
}
|
||||
},
|
||||
@@ -27,8 +22,7 @@
|
||||
},
|
||||
"global": {
|
||||
"copy_text": {
|
||||
"failed_to_copy": "클립보드에 복사하지 못했습니다",
|
||||
"https_required": "HTTPS 연결이 필요합니다"
|
||||
"failed_to_copy": "클립보드에 복사하지 못했습니다"
|
||||
},
|
||||
"date_time": {
|
||||
"ago": "{0} 전",
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
"item_photo": "Objektfoto",
|
||||
"item_quantity": "Objektantall",
|
||||
"parent_item": "Overordnet enhet",
|
||||
"rotate_photo": "Roter bilde",
|
||||
"rotate_photo": "Roter bile",
|
||||
"set_as_primary_photo": "Sett som { isPrimary, select, true {non-} false {} other {}}primary photo",
|
||||
"title": "Opprett objekt",
|
||||
"toast": {
|
||||
@@ -110,7 +110,7 @@
|
||||
"failed_load_parent": "Klarte ikke å laster overordnet enhet, vennligst velg en manuelt",
|
||||
"no_canvas_support": "Nettleseren din støtter ikke canvas-operasjoner",
|
||||
"please_select_location": "Vennligst velg en lokasjon.",
|
||||
"rotate_failed": "Fikk ikke til å rotere bildet: { error }",
|
||||
"rotate_failed": "Fikk ikke å rotere bildet: { error }",
|
||||
"rotate_process_failed": "Fikk ikke prosessert rotert bilde",
|
||||
"some_photos_failed": "{count, plural, =0 {Ingen bilder å laste opp.} =1 {1 fikk ikke lastet opp bilde.} other {Noen bilder ble ikke lastet opp.}}",
|
||||
"upload_failed": "Fikk ikke lastet opp bildet: { photoName }",
|
||||
@@ -121,9 +121,9 @@
|
||||
"uploaded": "Bilde lastet opp"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Ingen resultat funnet",
|
||||
"placeholder": "Velg…",
|
||||
"search_placeholder": "Skriv for å søke…"
|
||||
"no_results": "Ikke funnet resultat",
|
||||
"placeholder": "Velg...",
|
||||
"search_placeholder": "Skriv for å søke..."
|
||||
},
|
||||
"view": {
|
||||
"selectable": {
|
||||
@@ -149,7 +149,7 @@
|
||||
"title": "Opprett merkelapp",
|
||||
"toast": {
|
||||
"already_creating": "Opprettelse av etikett pågår allerede",
|
||||
"create_failed": "Kunne ikke opprette etikett",
|
||||
"create_failed": "Fikk ikke opprettet etikett",
|
||||
"create_success": "Etikett opprettet",
|
||||
"label_name_too_long": "Etikettnavn kan ikke overstige 50 tegn"
|
||||
}
|
||||
@@ -195,7 +195,7 @@
|
||||
"create_subitem": "Opprett underenhet",
|
||||
"created": "Opprettet",
|
||||
"delete": "Slett",
|
||||
"delete_confirm": "Er du sikker på at du ønsker å slette denne enheten? ",
|
||||
"delete_confirm": "Vil du virkelig slette denne enheten? ",
|
||||
"demo_instance": "Dette er et demomiljø",
|
||||
"details": "Detaljer",
|
||||
"duplicate": "Dupliser",
|
||||
@@ -368,7 +368,7 @@
|
||||
"updated_at": "Oppdatert den",
|
||||
"warranty": "Garanti",
|
||||
"warranty_details": "Garantidetaljer",
|
||||
"warranty_expires": "Garanti utløper"
|
||||
"warranty_expires": "Garanti upløper"
|
||||
},
|
||||
"labels": {
|
||||
"label_delete_confirm": "Er du sikker på at du vil slette denne etiketten? Denne handlingen kan ikke angres.",
|
||||
@@ -537,7 +537,7 @@
|
||||
"input_placeholder": "Skriv her",
|
||||
"instruction_1": "Homebox Label Generator er et verktøy som hjelper deg med å skrive ut etiketter for Homebox-oversikten din. Disse er ment\n som forhåndsutskrevne etiketter, slik at du kan skrive ut mange etiketter og ha dem klare til bruk",
|
||||
"instruction_2": "Etikettene fungerer ved å skrive ut en QR-kode med URL og AssetID-informasjon på etiketten. Hvis du har deaktivert\n AssetID-er i Homebox-innstillingene dine, kan du fortsatt bruke dette verktøyet, men AssetID-ene vil da ikke vise til noen gjenstand",
|
||||
"instruction_3": "Denne funksjonen er i en tidlig utviklingsfase og kan endres i fremtidige versjoner. Hvis du har tilbakemeldinger,\n vennligst legg dem inn i '<a href=\"https://github.com/sysadminsmedia/homebox/discussions/53\">'GitHub-diskusjonen'</a>'",
|
||||
"instruction_3": "Denne funksjonen er i en tidlig utviklingsfase og kan endres i fremtidige versjoner. Hvis du har tilbakemeldinger,\n vennligst legg dem inn i '<a href=\"https://github.com/sysadminsmedia/homebox/discussions/53\">GitHub-diskusjonen</a>'",
|
||||
"label_height": "Etiketthøyde",
|
||||
"label_width": "Etikettbredde",
|
||||
"measure_type": "Måltype",
|
||||
@@ -548,7 +548,7 @@
|
||||
"page_top_padding": "Toppmarg på siden",
|
||||
"page_width": "Sidebredde",
|
||||
"qr_code_example": "Eksempel på QR-kode",
|
||||
"tip_1": "Standardinnstillingene her er satt opp for\n'<a href=\"https://www.avery.com/templates/5260\">'Avery 5260 etikettark'</a>'. Hvis du bruker et annet ark,\nmå du justere innstillingene slik at de passer til ditt ark.",
|
||||
"tip_1": "Standardinnstillingene her er satt opp for\n‘<a href=\"https://www.avery.com/templates/5260\">Avery 5260 etikettark</a>’. Hvis du bruker et annet ark,\nmå du justere innstillingene slik at de passer til ditt ark.",
|
||||
"tip_2": "Hvis du tilpasser arket ditt, er målene oppgitt i tommer. Da jeg laget 5260-arket, oppdaget jeg at \nmålene som ble brukt i deres mal ikke stemte overens med det som faktisk trengtes for å skrive ut innenfor boksene.\n '<b>'Vær forberedt på litt prøving og feiling.'</b>'",
|
||||
"tip_3": "Når du skriver ut, må du sørge for å:\n '<ol><li>'Sett margen til 0 eller Ingen'</li><li>'Sett skaleringen til 100%'</li><li>'Deaktiver dobbeltsidig utskrift'</li><li>'Skriv ut en testside før du skriver ut flere sider'</li></ol>'",
|
||||
"tips": "Forslag",
|
||||
|
||||
@@ -100,8 +100,6 @@
|
||||
"item_photo": "Artikel Foto",
|
||||
"item_quantity": "Item aantal",
|
||||
"parent_item": "Hoofd Item",
|
||||
"product_tooltip_input_barcode": "Vul automatisch in met een handmatig verstrekte streepjescode",
|
||||
"product_tooltip_scan_barcode": "Vul automatisch in met een streepjescode van 📷",
|
||||
"rotate_photo": "Foto roteren",
|
||||
"set_as_primary_photo": "Instellen als { isPrimary, select, true {non-} false {} other {}}primaire foto",
|
||||
"title": "Maak object",
|
||||
@@ -122,15 +120,6 @@
|
||||
"upload_photos": "Foto’s toevoegen",
|
||||
"uploaded": "Geüploade foto"
|
||||
},
|
||||
"product_import": {
|
||||
"barcode": "Barcode van het product",
|
||||
"db_source": "DB-bron",
|
||||
"error_exception": "Er is een uitzondering opgetreden tijdens het ophalen van de streepjescode van het item: ",
|
||||
"error_invalid_barcode": "Ongeldige barcode opgegeven",
|
||||
"error_not_found": "Geen product gevonden met opgegeven barcode.",
|
||||
"search_item": "Zoek product",
|
||||
"title": "Importeer product"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Geen resultaten gevonden",
|
||||
"placeholder": "Selecteer…",
|
||||
@@ -195,9 +184,6 @@
|
||||
"shortcut_hint": "Gebruik de numerieke toetsen om snel een actie te selecteren."
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"api_failure": "Backend API call mislukt: "
|
||||
},
|
||||
"global": {
|
||||
"add": "Toevoegen",
|
||||
"archived": "Gearchiveerd",
|
||||
@@ -397,7 +383,6 @@
|
||||
"update_label": "Etiket bijwerken"
|
||||
},
|
||||
"languages": {
|
||||
"bs-BA": "Bosnisch (Bosnië en Herzegovina)",
|
||||
"ca": "Catalaans",
|
||||
"cs-CZ": "Tsjechisch",
|
||||
"de": "Duits",
|
||||
@@ -424,7 +409,6 @@
|
||||
"th-TH": "Thais",
|
||||
"tr": "Turks",
|
||||
"uk-UA": "Oekraïens",
|
||||
"vi-VN": "Vietnamees",
|
||||
"zh-CN": "Chinees (vereenvoudigd)",
|
||||
"zh-HK": "Chinees (Hong Kong)",
|
||||
"zh-MO": "Chinees (Macau)",
|
||||
@@ -553,7 +537,7 @@
|
||||
"input_placeholder": "Typ hier",
|
||||
"instruction_1": "De Homebox Label Generator is een hulpmiddel om u te helpen bij het afdrukken van labels voor uw Homebox-inventaris.\nDeze zijn bedoeld om op voorhand te printen zodat je een voorraad hebt en deze klaar zijn om te gebruiken",
|
||||
"instruction_2": "Als zodanig werken deze labels door een URL QR-code en item-ID-informatie op een label af te drukken. Als je item-ID's\nhebt uitgeschakeld in je Homebox-instellingen, kun je deze tool nog steeds gebruiken, maar zullen de item-ID's niet verwijzen naar een item",
|
||||
"instruction_3": "Deze functie bevindt zich in een vroege ontwikkelingsfase en kan in toekomstige releases veranderen. Als je feedback hebt, geef\n deze dan op in de '<a href=\"https://github.com/sysadminsmedia/homebox/discussions/53\">'GitHub-discussie'</a>'",
|
||||
"instruction_3": "Deze functie bevindt zich in een vroege ontwikkelingsfase en kan in toekomstige releases veranderen. Als je feedback hebt, geef\n deze dan op in de<a href=\"https://github.com/sysadminsmedia/homebox/discussions/53\"> ''GitHub-discussie '</a>'",
|
||||
"label_height": "Label hoogte",
|
||||
"label_width": "Label breedte",
|
||||
"measure_type": "Meting type",
|
||||
@@ -564,7 +548,7 @@
|
||||
"page_top_padding": "Top padding",
|
||||
"page_width": "Pagina breedte",
|
||||
"qr_code_example": "Voorbeeld QR Code",
|
||||
"tip_1": "De standaardinstellingen hier zijn ingesteld voor de\n '<a href=\"https://www.avery.com/templates/5260\">'Avery 5260-labelbladen'</a>'. Als je een ander vel gebruikt,\nmoet je de instellingen aanpassen aan jouw type vel.",
|
||||
"tip_1": "De standaardinstellingen hier zijn ingesteld voor de\n <a href=\"https://www.avery.com/templates/5260\">''Avery 5260-labelbladen '</a>'. Als je een ander vel gebruikt,\nmoet je de instellingen aanpassen aan jouw type vel.",
|
||||
"tip_2": "Als je het vel aanpast zijn de afmetingen in inch. Bij het maken van een 5260 vel, heb ik ontdekt dat de\nafmetingen in hun template niet overeen komen om te printen in de vakken.\n'<b>'Hou rekening met wat trial en error.'</b>'",
|
||||
"tip_3": "Let op bij het afdrukken:\n'<ol><li>'Stel de marges in op 0 of Geen'</li><li>'Zet de schaal op 100%'</li><li>'Dubbelzijdig afdrukken uitschakelen'</li><li>'Druk een testpagina af voordat u meerdere pagina's afdrukt'</li></ol>'",
|
||||
"tips": "Tips",
|
||||
@@ -575,8 +559,6 @@
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
"barcode_detected_message": "productbarcode gedetecteerd",
|
||||
"barcode_fetch_data": "Productgegevens ophalen",
|
||||
"error": "Tijdens het scannen is er een fout opgetreden",
|
||||
"invalid_url": "Ongeldige streepjescode",
|
||||
"no_sources": "Geen videobronnen beschikbaar",
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
{
|
||||
"components": {
|
||||
"app": {
|
||||
"create_modal": {
|
||||
"createAndAddAnother": "Użyj {shiftKey} + {enterKey}, aby utworzyć i dodać kolejny.",
|
||||
"enter": "Enter",
|
||||
"shift": "Shift"
|
||||
},
|
||||
"import_dialog": {
|
||||
"change_warning": "Zachowanie podczas importu z istniejącymi import_ref zostało zmienione. Jeśli import_ref jest obecny w pliku CSV, \nprzedmiot zostanie zaktualizowany wartościami z pliku CSV.",
|
||||
"description": "Zaimportuj plik CSV zawierający Twoje przedmioty, etykiety i lokalizacje. Zobacz dokumentację, aby uzyskać \nwięcej informacji na temat wymaganego formatu.",
|
||||
"title": "Zaimportuj plik CSV",
|
||||
"toast": {
|
||||
"import_failed": "Import zakończony niepowodzeniem. Spróbuj ponownie później.",
|
||||
"import_success": "Import zakończony sukcesem!",
|
||||
"please_select_file": "Wybierz plik do zaimportowania."
|
||||
}
|
||||
"title": "Zaimportuj plik CSV"
|
||||
},
|
||||
"outdated": {
|
||||
"current_version": "Bieżąca wersja",
|
||||
@@ -24,18 +14,6 @@
|
||||
"new_version_available_link": "Kliknij tutaj, aby wyświetlić informacje o wydaniu"
|
||||
}
|
||||
},
|
||||
"color_selector": {
|
||||
"clear": "Wyczyść kolor",
|
||||
"color": "Kolor",
|
||||
"no_color": "Brak koloru",
|
||||
"no_color_selected": "Żaden kolor nie został wybrany",
|
||||
"randomize": "Losuj kolor"
|
||||
},
|
||||
"form": {
|
||||
"password": {
|
||||
"toggle_show": "Przełącz wyświetlanie hasła"
|
||||
}
|
||||
},
|
||||
"global": {
|
||||
"copy_text": {
|
||||
"documentation": "dokumentacja",
|
||||
@@ -73,12 +51,7 @@
|
||||
"download": "Pobierz etykietę",
|
||||
"print": "Drukuj etykietę",
|
||||
"server_print": "Drukuj na serwerze",
|
||||
"titles": "Etykiety",
|
||||
"toast": {
|
||||
"load_status_failed": "Nie udało się załadować statusu",
|
||||
"print_failed": "Nie udało się wydrukować etykiety",
|
||||
"print_success": "Etykieta wydrukowana"
|
||||
}
|
||||
"titles": "Etykiety"
|
||||
},
|
||||
"page_qr_code": {
|
||||
"page_url": "Adres URL strony",
|
||||
@@ -89,52 +62,12 @@
|
||||
}
|
||||
},
|
||||
"item": {
|
||||
"attachments_list": {
|
||||
"download": "Pobierz",
|
||||
"open_new_tab": "Otwórz w nowej karcie"
|
||||
},
|
||||
"create_modal": {
|
||||
"delete_photo": "Usuń zdjęcie",
|
||||
"item_description": "Opis przedmiotu",
|
||||
"item_name": "Nazwa przedmiotu",
|
||||
"item_photo": "Zdjęcie Przedmiotu 📷",
|
||||
"item_quantity": "Ilość przedmiotu",
|
||||
"parent_item": "Przedmiot nadrzędny",
|
||||
"product_tooltip_input_barcode": "Autouzupełnianie za pomocą ręcznie dostarczonego kodu kreskowego",
|
||||
"product_tooltip_scan_barcode": "Autouzupełnianie kodem kreskowym z 📷",
|
||||
"rotate_photo": "Obróć zdjęcie",
|
||||
"set_as_primary_photo": "Ustaw jako { isPrimary, select, true {non-} false {} other {}}główne zdjęcie",
|
||||
"title": "Utwórz przedmiot",
|
||||
"toast": {
|
||||
"already_creating": "Przedmiot w trakcie tworzenia",
|
||||
"create_failed": "Nie udało się utworzyć przedmiotu",
|
||||
"create_success": "Przedmiot utworzony",
|
||||
"failed_load_parent": "Nie udało się załadować przedmiotu nadrzędnego - proszę wybrać go ręcznie",
|
||||
"no_canvas_support": "Twoja przeglądarka nie wspiera operacji przy użyciu canvas",
|
||||
"please_select_location": "Proszę wybrać lokalizację.",
|
||||
"rotate_failed": "Nie udało się obrócić zdjęcia: { error }",
|
||||
"rotate_process_failed": "Nie udało się przetworzyć obróconego zdjęcia",
|
||||
"some_photos_failed": "{count, plural, =0 {Brak zdjęć do przesłania.} =1 {Nie udało się przesłać 1 zdjęcia.} other {Nie udało się przesłać niektórych zdjęć.}}",
|
||||
"upload_failed": "Nie udało się wysłać zdjęcia: { photoName }",
|
||||
"upload_success": "{count, plural, =0 {Nie przesłano zdjęć.} =1 {Zdjęcie zostało pomyślnie przesłane.} other {Wszystkie zdjęcia zostały pomyślnie przesłane.}}",
|
||||
"uploading_photos": "{count, plural, =0 {Brak zdjęć do przesłania} =1 {Przesyłanie 1 zdjęcia…} other {Przesyłanie {count} zdjęć…}}"
|
||||
},
|
||||
"upload_photos": "Prześlij Zdjęcia",
|
||||
"uploaded": "Przesłane Zdjęcie"
|
||||
},
|
||||
"product_import": {
|
||||
"barcode": "Kod Kreskowy Produktu",
|
||||
"db_source": "Źródło bazy danych",
|
||||
"error_exception": "Wystąpił wyjątek podczas pobierania kodu kreskowego pozycji: ",
|
||||
"error_invalid_barcode": "Podano nieprawidłowy kod kreskowy",
|
||||
"error_not_found": "Nie znaleziono produktu z podanym kodem kreskowym.",
|
||||
"search_item": "Wyszukaj produkt",
|
||||
"title": "Importuj produkt"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Brak rezultatów",
|
||||
"placeholder": "Wybierz…",
|
||||
"search_placeholder": "Wpisz, aby wyszukać…"
|
||||
"upload_photos": "Prześlij Zdjęcia"
|
||||
},
|
||||
"view": {
|
||||
"selectable": {
|
||||
@@ -147,23 +80,15 @@
|
||||
"headers": "Nagłówki",
|
||||
"page": "Strona",
|
||||
"rows_per_page": "Ilość wierszy na stronę",
|
||||
"table_settings": "Ustawienia Tabeli",
|
||||
"view_item": "Zobacz przedmiot"
|
||||
"table_settings": "Ustawienia Tabeli"
|
||||
}
|
||||
}
|
||||
},
|
||||
"label": {
|
||||
"create_modal": {
|
||||
"label_color": "Kolor etykiety",
|
||||
"label_description": "Opis etykiety",
|
||||
"label_name": "Nazwa etykiety",
|
||||
"title": "Stwórz nową etykietę",
|
||||
"toast": {
|
||||
"already_creating": "Etykieta jest w trakcie tworzenia",
|
||||
"create_failed": "Nie udało się utworzyć etykiety",
|
||||
"create_success": "Etykieta utworzona",
|
||||
"label_name_too_long": "Etykieta nie może być dłuższa niż 50 znaków"
|
||||
}
|
||||
"title": "Stwórz nową etykietę"
|
||||
},
|
||||
"selector": {
|
||||
"select_labels": "Wybierz Etykiety"
|
||||
@@ -173,12 +98,7 @@
|
||||
"create_modal": {
|
||||
"location_description": "Opis lokalizacji",
|
||||
"location_name": "Nazwa lokalizacji",
|
||||
"title": "Utwórz lokalizację",
|
||||
"toast": {
|
||||
"already_creating": "Lokalizacja jest w trakcie tworzenia",
|
||||
"create_failed": "Nie udało się utworzyć lokalizacji",
|
||||
"create_success": "Lokalizacja utworzona"
|
||||
}
|
||||
"title": "Utwórz lokalizację"
|
||||
},
|
||||
"selector": {
|
||||
"no_location_found": "Nie znaleziono żadnej lokalizacji",
|
||||
@@ -187,7 +107,7 @@
|
||||
"select_location": "Wybierz lokalizację"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "Brak dostępnych lokalizacji. Dodaj nowe lokalizacje za pomocą przycisku\n '<span class=\"link-primary\">Utwórz lokalizację</span>' na pasku nawigacyjnym."
|
||||
"no_locations": "Brak dostępnych lokalizacji. Dodaj nowe lokalizacje poprzez przycisk\n `<`span class=\"link-primary\"`>`Utwórz`<`/span`>` na pasku nawigacyjnym."
|
||||
}
|
||||
},
|
||||
"quick_menu": {
|
||||
@@ -195,9 +115,6 @@
|
||||
"shortcut_hint": "Użyj klawiszy numerycznych, aby szybko wybrać akcję."
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"api_failure": "Wywołanie interfejsu API zaplecza nie powiodło się: "
|
||||
},
|
||||
"global": {
|
||||
"add": "Dodaj",
|
||||
"archived": "Zarchiwizowane",
|
||||
@@ -206,11 +123,8 @@
|
||||
"confirm": "Potwierdź",
|
||||
"create": "Utwórz",
|
||||
"create_and_add": "Utwórz i dodaj kolejny",
|
||||
"create_subitem": "Utwórz podrzędny przedmiot",
|
||||
"created": "Utworzone",
|
||||
"delete": "Usuń",
|
||||
"delete_confirm": "Czy na pewno chcesz usunąć ten przedmiot? ",
|
||||
"demo_instance": "To jest wersja demonstracyjna",
|
||||
"details": "Szczegóły",
|
||||
"duplicate": "Duplikat",
|
||||
"edit": "Edytuj",
|
||||
@@ -225,7 +139,6 @@
|
||||
"items": "Przedmioty",
|
||||
"join_discord": "Dołącz do Discorda",
|
||||
"labels": "Etykiety",
|
||||
"loading": "Wczytywanie…",
|
||||
"locations": "Lokalizacje",
|
||||
"maintenance": "Konserwacja",
|
||||
"name": "Nazwa",
|
||||
@@ -233,14 +146,11 @@
|
||||
"password": "Hasło",
|
||||
"quantity": "Ilość",
|
||||
"read_docs": "Przeczytaj dokumentację",
|
||||
"return_home": "Powrót do strony głównej",
|
||||
"save": "Zapisz",
|
||||
"search": "Wyszukaj",
|
||||
"sign_out": "Wyloguj się",
|
||||
"submit": "Wyślij",
|
||||
"unknown": "Nieznany",
|
||||
"update": "Aktualizuj",
|
||||
"updating": "Aktualizacja",
|
||||
"value": "Wartość",
|
||||
"version": "Wersja:{version}",
|
||||
"welcome": "Witaj, {username}"
|
||||
@@ -265,49 +175,27 @@
|
||||
"set_email": "Jaki jest Twój adres e-mail?",
|
||||
"set_name": "Jak się nazywasz?",
|
||||
"set_password": "Ustaw swoje hasło",
|
||||
"tagline": "Śledź, organizuj i zarządzaj swoimi rzeczami.",
|
||||
"title": "Zorganizuj i oznacz swoje rzeczy",
|
||||
"toast": {
|
||||
"invalid_email": "Nieprawidłowy adres e-mail",
|
||||
"invalid_email_password": "Nieprawidłowy adres e-mail lub hasło",
|
||||
"login_success": "Zalogowano pomyślnie",
|
||||
"problem_registering": "Problem z rejestracją użytkownika",
|
||||
"user_registered": "Użytkownik zarejestrowany"
|
||||
}
|
||||
"tagline": "Śledź, organizuj i zarządzaj swoimi rzeczami."
|
||||
},
|
||||
"items": {
|
||||
"add": "Dodaj",
|
||||
"advanced": "Zaawansowane",
|
||||
"archived": "Zarchiwizowane",
|
||||
"asset_id": "Identyfikator zasobu",
|
||||
"associated_with_multiple": "Ten identyfikator zasobu jest powiązany z wieloma przedmiotami",
|
||||
"attachment": "Załącznik",
|
||||
"attachments": "Załączniki",
|
||||
"changes_persisted_immediately": "Zmiany w załącznikach zostaną natychmiast zapisane",
|
||||
"created_at": "Data utworzenia",
|
||||
"custom_fields": "Pola niestandardowe",
|
||||
"delete_attachment_confirm": "Czy na pewno chcesz usunąć ten załącznik?",
|
||||
"delete_item_confirm": "Czy na pewno chcesz usunąć ten przedmiot?",
|
||||
"description": "Opis",
|
||||
"details": "Szczegóły",
|
||||
"drag_and_drop": "Przeciągnij i upuść pliki tutaj lub kliknij, aby wybrać pliki",
|
||||
"edit": {
|
||||
"edit_attachment_dialog": {
|
||||
"attachment_title": "Tytuł załącznika",
|
||||
"attachment_type": "Typ załącznika",
|
||||
"primary_photo": "Główne zdjęcie",
|
||||
"primary_photo_sub": "Ta opcja jest dostępna tylko dla zdjęć. Tylko jedno zdjęcie może być zdjęciem głównym. Po wybraniu tej opcji, bieżące zdjęcie główne (jeśli takie istnieje) zostanie odznaczone.",
|
||||
"select_type": "Wybierz typ",
|
||||
"title": "Edycja załącznika"
|
||||
}
|
||||
},
|
||||
"edit_details": "Edytuj szczegóły",
|
||||
"field_selector": "Selektor pól",
|
||||
"field_value": "Wartość pola",
|
||||
"first": "Pierwszy",
|
||||
"include_archive": "Uwzględnij zarchiwizowane przedmioty",
|
||||
"insured": "Ubezpieczony",
|
||||
"invalid_asset_id": "Nieprawidłowy identyfikator zasobu",
|
||||
"last": "Ostatni",
|
||||
"lifetime_warranty": "Dożywotnia gwarancja",
|
||||
"location": "Lokalizacja",
|
||||
@@ -318,7 +206,6 @@
|
||||
"name": "Nazwa",
|
||||
"negate_labels": "Neguj wybrane etykiety",
|
||||
"next_page": "Następna strona",
|
||||
"no_attachments": "Nie znaleziono załączników",
|
||||
"no_results": "Nie znaleziono przedmiotów",
|
||||
"notes": "Notatki",
|
||||
"only_with_photo": "Tylko przedmioty ze zdjęciem",
|
||||
@@ -340,64 +227,27 @@
|
||||
"receipts": "Paragony",
|
||||
"reset_search": "Zresetuj wyszukiwanie",
|
||||
"results": "{ total } wyników",
|
||||
"select_field": "Wybierz pole",
|
||||
"serial_number": "Numer seryjny",
|
||||
"show_advanced_view_options": "Pokaż ustawienia zaawansowane",
|
||||
"sold_at": "Sprzedane w",
|
||||
"sold_details": "Szczegóły sprzedaży",
|
||||
"sold_price": "Cena sprzedaży",
|
||||
"sold_to": "Sprzedane do",
|
||||
"sync_child_locations": "Synchronizuj lokalizacje przedmiotów podrzędnych",
|
||||
"tip_1": "Filtry lokalizacji i etykiet używają operacji 'LUB'. Jeśli wybrano więcej niż jeden, wystarczy jeden, \naby uzyskać dopasowanie.",
|
||||
"tip_2": "Wyszukiwania poprzedzone prefiksem \"#\" będą wysyłać zapytanie o identyfikator zasobu (na przykład \"#000-001\")",
|
||||
"tip_3": "Filtry pól używają operacji 'LUB'. Jeśli wybrano więcej niż jeden, wystarczy jeden, \naby uzyskać dopasowanie.",
|
||||
"tips": "Wskazówki",
|
||||
"tips_sub": "Wskazówki wyszukiwania",
|
||||
"toast": {
|
||||
"asset_not_found": "Nie znaleziono przedmiotu",
|
||||
"attachment_deleted": "Załącznik został usunięty",
|
||||
"attachment_updated": "Załącznik zaktualizowany",
|
||||
"attachment_uploaded": "Załącznik przesłany",
|
||||
"child_items_location_no_longer_synced": "Lokalizacje przedmiotów podrzędnych nie będą już synchronizowane z tym przedmiotem.",
|
||||
"child_items_location_synced": "Lokalizacje przedmiotów podrzędnych zostały zsynchronizowane z tym przedmiotem",
|
||||
"child_location_desync": "Zmiana lokalizacji spowoduje desynchronizację z lokalizacją przedmiotu nadrzędnego",
|
||||
"error_loading_parent_data": "Wystąpił błąd podczas próby załadowania danych nadrzędnych",
|
||||
"failed_adjust_quantity": "Nie udało się dostosować ilości",
|
||||
"failed_delete_attachment": "Nie udało się usunąć załącznika",
|
||||
"failed_delete_item": "Nie udało się usunąć przedmiotu",
|
||||
"failed_duplicate_item": "Nie udało się zduplikować przedmiotu",
|
||||
"failed_load_asset": "Nie udało się załadować przedmiotu",
|
||||
"failed_load_item": "Nie udało się załadować przedmiotu",
|
||||
"failed_load_items": "Nie udało się załadować przedmiotów",
|
||||
"failed_save": "Nie udało się zapisać przedmiotu",
|
||||
"failed_save_no_location": "Nie udało się zapisać przedmiotu: nie wybrano lokalizacji",
|
||||
"failed_search_items": "Nie udało się wyszukać przedmiotów",
|
||||
"failed_update_attachment": "Nie udało się zaktualizować załącznika",
|
||||
"failed_upload_attachment": "Nie udało się przesłać załącznika",
|
||||
"item_deleted": "Przedmiot usunięty",
|
||||
"item_saved": "Przedmiot zapisany",
|
||||
"quantity_cannot_negative": "Ilość nie może być ujemna",
|
||||
"sync_child_location": "Wybrany przedmiot nadrzędny synchronizuje lokalizacje swoich dzieci ze swoimi. Lokalizacja została zaktualizowana."
|
||||
},
|
||||
"updated_at": "Zaktualizowano",
|
||||
"warranty": "Gwarancja",
|
||||
"warranty_details": "Szczegóły gwarancji",
|
||||
"warranty_expires": "Gwarancja wygasa"
|
||||
},
|
||||
"labels": {
|
||||
"label_delete_confirm": "Czy na pewno chcesz usunąć tę etykietę? Tej czynności nie można cofnąć.",
|
||||
"no_results": "Nie znaleziono etykiet",
|
||||
"toast": {
|
||||
"failed_delete_label": "Nie udało się usunąć etykiety",
|
||||
"failed_load_label": "Nie udało się załadować etykiety",
|
||||
"failed_update_label": "Nie udało się zaktualizować etykiety",
|
||||
"label_deleted": "Etykieta usunięta",
|
||||
"label_updated": "Etykieta zaktualizowana"
|
||||
},
|
||||
"update_label": "Aktualizuj etykietę"
|
||||
},
|
||||
"languages": {
|
||||
"bs-BA": "Bośniacki (Bośnia i Hercegowina)",
|
||||
"ca": "kataloński",
|
||||
"cs-CZ": "Czeski",
|
||||
"de": "niemiecki",
|
||||
@@ -410,8 +260,6 @@
|
||||
"it": "włoski",
|
||||
"ja-JP": "japoński",
|
||||
"ko-KR": "Koreański",
|
||||
"lb-LU": "Luksemburski (Luksemburg)",
|
||||
"lt-LT": "Litewski (Litwa)",
|
||||
"nb-NO": "Norweski Bokmål",
|
||||
"nl": "holenderski",
|
||||
"pl": "polski",
|
||||
@@ -424,7 +272,6 @@
|
||||
"th-TH": "Tajski",
|
||||
"tr": "turecki",
|
||||
"uk-UA": "ukraiński",
|
||||
"vi-VN": "Wietnamski",
|
||||
"zh-CN": "chiński (uproszczony)",
|
||||
"zh-HK": "chiński (Hong Kong)",
|
||||
"zh-MO": "chiński (Makau)",
|
||||
@@ -438,15 +285,7 @@
|
||||
"child_locations": "Podlokalizacje",
|
||||
"collapse_tree": "Zwiń drzewo",
|
||||
"expand_tree": "Rozwiń Drzewo",
|
||||
"location_items_delete_confirm": "Czy na pewno chcesz usunąć tę lokalizację i wszystkie jej przedmioty? Tej czynności nie można cofnąć.",
|
||||
"no_results": "Nie znaleziono lokalizacji",
|
||||
"toast": {
|
||||
"failed_delete_location": "Nie udało się usunąć lokalizacji",
|
||||
"failed_load_location": "Nie udało się załadować lokalizacji",
|
||||
"failed_update_location": "Nie udało się zaktualizować lokalizacji",
|
||||
"location_deleted": "Lokalizacja usunięta",
|
||||
"location_updated": "Lokalizacja zaktualizowana"
|
||||
},
|
||||
"update_location": "Zaktualizuj lokalizację"
|
||||
},
|
||||
"maintenance": {
|
||||
@@ -502,10 +341,7 @@
|
||||
"currency_format": "Format waluty",
|
||||
"current_password": "Bieżące hasło",
|
||||
"delete_account": "Usuń konto",
|
||||
"delete_account_confirm": "Czy na pewno chcesz usunąć swoje konto? Jeśli jesteś ostatnim członkiem grupy, wszystkie Twoje dane zostaną usunięte. Tej czynności nie można cofnąć.",
|
||||
"delete_account_sub": "Usuń swoje konto oraz wszystkie powiązane z nim dane. Tego nie można cofnąć.",
|
||||
"delete_notifier_confirm": "Czy na pewno chcesz usunąć tego powiadamiacza?",
|
||||
"display_legacy_header": "{ currentValue, select, true {Wyłącz starszy nagłówek} false {Włącz starszy nagłówek} other {Brak wartości}}",
|
||||
"enabled": "Włączone",
|
||||
"example": "Przykład",
|
||||
"gen_invite": "Wygeneruj link z zaproszeniem",
|
||||
@@ -515,72 +351,22 @@
|
||||
"language": "Język",
|
||||
"new_password": "Nowe hasło",
|
||||
"no_notifiers": "Nie skonfigurowano powiadomień",
|
||||
"no_override": "Bez nadpisania",
|
||||
"notifier_modal": "{type, select, true {Edytuj} false {Utwórz} other {Inny}} Powiadomiacz",
|
||||
"notifiers": "Powiadomiacze",
|
||||
"notifiers_sub": "Otrzymuj powiadomienia o nadchodzących przypomnieniach o konserwacji",
|
||||
"override_locale": "Zastąp język daty i waluty",
|
||||
"test": "Test",
|
||||
"theme_settings": "Ustawienia tematu",
|
||||
"theme_settings_sub": "Ustawienia motywu są przechowywane w lokalnej pamięci przeglądarki. Możesz zmienić motyw w dowolnym momencie. \nJeśli masz problemy z ustawieniem motywu, spróbuj odświeżyć przeglądarkę.",
|
||||
"toast": {
|
||||
"account_deleted": "Twoje konto zostało usunięte.",
|
||||
"failed_change_password": "Nie udało się zmienić hasła.",
|
||||
"failed_create_notifier": "Nie udało się utworzyć powiadamiacza.",
|
||||
"failed_delete_account": "Nie udało się usunąć konta.",
|
||||
"failed_delete_notifier": "Nie udało się usunąć powiadamiacza.",
|
||||
"failed_get_currencies": "Nie udało się pobrać walut",
|
||||
"failed_test_notifier": "Nie udało się przetestować powiadamiacza.",
|
||||
"failed_update_group": "Nie udało się zaktualizować grupy",
|
||||
"failed_update_notifier": "Nie udało się zaktualizować powiadamiacza.",
|
||||
"group_updated": "Grupa zaktualizowana",
|
||||
"notifier_test_success": "Test powiadamiania zakończony pomyślnie.",
|
||||
"password_changed": "Hasło zostało pomyślnie zmienione."
|
||||
},
|
||||
"update_group": "Zaktualizuj grupę",
|
||||
"update_language": "Aktualizuj język",
|
||||
"url": "Adres URL",
|
||||
"user_profile": "Profil użytkownika",
|
||||
"user_profile_sub": "Zaproś użytkowników i zarządzaj swoim kontem."
|
||||
},
|
||||
"reports": {
|
||||
"label_generator": {
|
||||
"asset_end": "Ostatni przedmiot",
|
||||
"asset_start": "Pierwszy przedmiot",
|
||||
"base_url": "URL do strony",
|
||||
"bordered_labels": "Etykiety obramowane",
|
||||
"generate_page": "Wygeneruj stronę",
|
||||
"input_placeholder": "Wpisz",
|
||||
"instruction_1": "Generator etykiet Homebox to narzędzie, które pomoże Ci drukować etykiety do twojej kolekcji. Są to etykiety do\ndruku z wyprzedzeniem, dzięki czemu możesz wydrukować wiele etykiet i mieć je gotowe do użycia",
|
||||
"instruction_2": "W związku z tym etykiety te działają poprzez drukowanie kodu QR URL i informacji o AssetID na etykiecie. \nJeśli wyłączyłeś/aś AssetID w ustawieniach Homebox, nadal możesz korzystać z tego narzędzia, ale AssetID nie będzie odnosić się do żadnego elementu",
|
||||
"instruction_3": "Ta funkcja jest na wczesnym etapie rozwoju i może ulec zmianie w przyszłych wersjach. Jeśli masz jakieś uwagi, prześlij je\nw '<a href=\"https://github.com/sysadminsmedia/homebox/discussions/53\">'dyskusji na GitHubie'</a>'",
|
||||
"label_height": "Wysokość etykiety",
|
||||
"label_width": "Szerokość etykiety",
|
||||
"measure_type": "Typ miary",
|
||||
"page_bottom_padding": "Wypełnienie dolnej części strony",
|
||||
"page_height": "Wysokość strony",
|
||||
"page_left_padding": "Wypełnienie lewej strony",
|
||||
"page_right_padding": "Wypełnienie prawej strony",
|
||||
"page_top_padding": "Wypełnienie górnej części strony",
|
||||
"page_width": "Szerokość strony",
|
||||
"qr_code_example": "Przykład kodu QR",
|
||||
"tip_1": "Ustawienia domyślne są skonfigurowane dla\n'<a href=\"https://www.avery.com/templates/5260\">'arkuszy etykiet Avery 5260'</a>'. Jeśli używasz innego arkusza,\nmusisz dostosować ustawienia do swojego arkusza.",
|
||||
"tip_2": "Jeśli dostosowujesz arkusz, jego wymiary są podane w calach. Podczas tworzenia arkusza 5260 zauważyłem, że\nwymiary użyte w ich szablonie nie odpowiadały wymiarom potrzebnym do wydrukowania w polach.\n'<b>'Przygotuj się na metodę prób i błędów.'</b>'",
|
||||
"tip_3": "Podczas drukowania pamiętaj o:\n'<ol><li>'Ustaw marginesy na 0 lub Brak'</li><li>'Ustaw skalowanie na 100%'</li><li>'Wyłącz drukowanie dwustronne'</li><li>'Wydrukuj stronę testową przed wydrukowaniem wielu stron'</li></ol>'",
|
||||
"tips": "Wskazówki",
|
||||
"title": "Generator etykiet",
|
||||
"toast": {
|
||||
"page_too_small_card": "Rozmiar strony jest za mały w stosunku do rozmiaru karty"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
"barcode_detected_message": "wykryto kod kreskowy produktu",
|
||||
"barcode_fetch_data": "Pobierz dane produktu",
|
||||
"error": "Wystąpił błąd podczas skanowania",
|
||||
"invalid_url": "Nieprawidłowy URL kodu kreskowego",
|
||||
"no_sources": "Brak dostępnych źródeł wideo",
|
||||
"permission_denied": "Odmowa dostępu do aparatu, zezwól na dostęp do aparatu w ustawieniach przeglądarki",
|
||||
"select_video_source": "Wybierz źródło wideo",
|
||||
"title": "Skaner",
|
||||
"unsupported": "Media Stream API nie jest obsługiwany bez HTTPS"
|
||||
@@ -588,25 +374,18 @@
|
||||
"tools": {
|
||||
"actions": "Akcje na zasobach",
|
||||
"actions_set": {
|
||||
"create_missing_thumbnails": "Utwórz brakujące miniatury",
|
||||
"create_missing_thumbnails_button": "Tworzenie miniaturek",
|
||||
"create_missing_thumbnails_confirm": "Czy na pewno chcesz utworzyć brakujące miniatury? Może to chwilę potrwać i nie można tego wstrzymać.",
|
||||
"create_missing_thumbnails_sub": "Tworzy miniatury dla wszystkich załączników obsługiwanych przez bieżącą konfigurację. Jest to przydatne w przypadku załączników przesłanych przed wersją b0.20.0 Homebox. Nie spowoduje to nadpisania istniejących miniatur, a jedynie utworzenie nowych dla załączników, które nie mają miniatur. Należy pamiętać, że miniatury są tworzone w tle i ich utworzenie może chwilę potrwać.",
|
||||
"ensure_ids": "Zapewnienie identyfikatorów zasobów",
|
||||
"ensure_ids_button": "Zapewnij identyfikatory zasobów",
|
||||
"ensure_ids_confirm": "Czy na pewno chcesz, aby wszystkie zasoby miały identyfikatory? Może to chwilę potrwać i nie można tego cofnąć.",
|
||||
"ensure_ids_sub": "Zapewnia, że wszystkie przedmioty w Twoim inwentarzu mają prawidłowe pole asset_id. W tym celu wyszukiwany jest najwyższy obecny asset_id w bazie danych, a kolejne wartości są przypisywane do przedmiotów, które nie mają ustawionego pola asset_id. Proces odbywa się w kolejności według pola created_at.",
|
||||
"ensure_import_refs": "Zapewnij odnośniki importu",
|
||||
"ensure_import_refs_button": "Zapewnij odnośniki importu",
|
||||
"ensure_import_refs_sub": "Zapewnia, że wszystkie przedmioty w Twoim inwentarzu mają prawidłowe pole import_ref. W tym celu dla każdego przedmiotu, który nie ma ustawionego pola import_ref, losowo generowany jest 8-znakowy ciąg.",
|
||||
"set_primary_photo": "Ustaw główne zdjęcie",
|
||||
"set_primary_photo_button": "Ustaw główne zdjęcie",
|
||||
"set_primary_photo_confirm": "Czy na pewno chcesz ustawić zdjęcia główne? To może chwilę potrwać i nie można tego cofnąć.",
|
||||
"set_primary_photo_sub": "W wersji v0.10.0 Homebox pole głównego obrazu zostało dodane do załączników typu zdjęcie. Ta akcja ustawi pole głównego obrazu do pierwszego zdjęcia w załącznikach z bazy danych, jeżeli nie jest już ustawione. '<a class=\"link\" href=\"https://github.com/hay-kot/homebox/pull/576\">'Zobacz GitHub PR #576'</a>'",
|
||||
"zero_datetimes": "Wyzeruj daty i godziny przedmiotów",
|
||||
"zero_datetimes_button": "Wyzeruj daty i godziny przedmiotów",
|
||||
"zero_datetimes_confirm": "Czy na pewno chcesz zresetować wszystkie wartości daty i godziny? Może to chwilę potrwać i nie można tego cofnąć.",
|
||||
"zero_datetimes_sub": "Resetuje wartość czasu dla wszystkich pól daty i godziny w Twoim inwentarzu, ustawiając ją na początek dnia. Jest to rozwiązanie problemu, który pojawił się we wczesnej fazie rozwoju aplikacji, powodując zapisywanie wartości czasu wraz z datą, co skutkowało nieprawidłowym wyświetlaniem wartości w polach daty. '<a class=\"link\" href=\"https://github.com/hay-kot/homebox/issues/236\" target=\"_blank\">'Zobacz szczegóły w GitHub Issue #236'</a>'"
|
||||
"zero_datetimes_sub": "Resetuje wartość czasu dla wszystkich pól daty i godziny w Twoim inwentarzu, ustawiając ją na początek dnia. Jest to rozwiązanie problemu, który pojawił się we wczesnej fazie rozwoju aplikacji, powodując zapisywanie wartości czasu wraz z datą, co skutkowało nieprawidłowym wyświetlaniem wartości w polach daty. '<a class=\"link\" href=\"https://github.com/hay-kot/homebox/issues/236\" target=\"_blank\">Zobacz szczegóły w GitHub Issue #236</a>'"
|
||||
},
|
||||
"actions_sub": "Zastosuj akcje zbiorczo do swojego inwentarza. Są to działania nieodwracalne. '<b>'Bądź ostrożny.'</b>",
|
||||
"import_export": "Import/eksport",
|
||||
@@ -616,7 +395,6 @@
|
||||
"export_sub": "Eksportuje standardowy format CSV dla Homebox. Zostaną wyeksportowane wszystkie przedmioty z Twojego inwentarza.",
|
||||
"import": "Importuj inwentarz",
|
||||
"import_button": "Importuj inwentarz",
|
||||
"import_ref_confirm": "Czy na pewno chcesz, aby wszystkie zasoby miały import_ref? Może to chwilę potrwać i nie można tego cofnąć.",
|
||||
"import_sub": "Importuje standardowy format CSV dla Homebox. Jeśli plik nie zawiera kolumny '<code>'HB.import_ref'</code>', istniejące przedmioty w Twoim inwentarzu '<b>'nie'</b>' zostaną nadpisane – zostaną dodane jedynie nowe przedmioty. Wiersze z kolumną '<code>'HB.import_ref'</code>' są łączone z istniejącymi przedmiotami, które mają ten sam import_ref, jeśli taki istnieje."
|
||||
},
|
||||
"import_export_sub": "Importuj i eksportuj swój inwentarz z i do pliku CSV. Jest to przydatne podczas przenoszenia inwentarza do nowej instancji Homebox.",
|
||||
@@ -629,14 +407,6 @@
|
||||
"bill_of_materials_button": "Generuj zestawienie materiałów",
|
||||
"bill_of_materials_sub": "Generuje plik CSV (Comma Separated Values), który można zaimportować do programu arkusza kalkulacyjnego. Jest to podsumowanie Twojego inwentarza z podstawowymi informacjami o przedmiotach i cenach."
|
||||
},
|
||||
"reports_sub": "Generuj różne raporty dla swojego inwentarza.",
|
||||
"toast": {
|
||||
"asset_success": "Zaktualizowano { results } przedmiotów.",
|
||||
"failed_create_missing_thumbnails": "Nie udało się utworzyć brakujących miniatur.",
|
||||
"failed_ensure_ids": "Nie udało się sprawdzić identyfikatorów zasobów.",
|
||||
"failed_ensure_import_refs": "Nie udało się zapewnić import_ref.",
|
||||
"failed_set_primary_photos": "Nie udało się ustawić głównych zdjęć.",
|
||||
"failed_zero_datetimes": "Nie udało się zresetować wartości daty i godziny."
|
||||
}
|
||||
"reports_sub": "Generuj różne raporty dla swojego inwentarza."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,19 @@
|
||||
{
|
||||
"components": {
|
||||
"app": {
|
||||
"create_modal": {
|
||||
"createAndAddAnother": "Use {shiftKey} + {enterKey} para criar e adicionar outro.",
|
||||
"enter": "Entrar",
|
||||
"shift": "Continuar"
|
||||
},
|
||||
"import_dialog": {
|
||||
"change_warning": "O comportamento das importações com import_refs existentes foi alterado. Se houver um import_ref presente no arquivo CSV, o \nitem será atualizado com os valores presentes no arquivo CSV.",
|
||||
"description": "Importe um arquivo CSV contendo seus itens, etiquetas e locais. Consulte a documentação para mais informações \nsobre a formatação necessária.",
|
||||
"title": "Importar arquivo CSV",
|
||||
"toast": {
|
||||
"import_failed": "Importação falhou. Tente novamente mais tarde.",
|
||||
"import_success": "Importação bem-sucedida!",
|
||||
"please_select_file": "Por favor, selecione um arquivo para importar."
|
||||
}
|
||||
"change_warning": "O comportamento de importações com import_refs existentes foi alterado. Se houver um import_ref presente no arquivo CSV, o\nitem será atualizado com os valores presentes no arquivo CSV.",
|
||||
"description": "Importe um arquivo CSV contendo seus items, etiquetas e locais. Consulte a documentação para mais informações\nsobre a formatação aceita.",
|
||||
"title": "Importar arquivo CSV"
|
||||
},
|
||||
"outdated": {
|
||||
"current_version": "Versão Atual",
|
||||
"dismiss": "Fechar",
|
||||
"dismiss": "Dispensar",
|
||||
"latest_version": "Última Versão",
|
||||
"new_version_available": "Nova Versão Disponível",
|
||||
"new_version_available_link": "Clique aqui para ver as novidades"
|
||||
}
|
||||
},
|
||||
"color_selector": {
|
||||
"clear": "Remover cor",
|
||||
"color": "Cor",
|
||||
"no_color": "Sem cor",
|
||||
"no_color_selected": "Nenhuma cor selecionada",
|
||||
"randomize": "Cor aleatória"
|
||||
},
|
||||
"form": {
|
||||
"password": {
|
||||
"toggle_show": "Alternar Mostrar Senha"
|
||||
}
|
||||
},
|
||||
"global": {
|
||||
"copy_text": {
|
||||
"documentation": "documentação",
|
||||
@@ -73,12 +51,7 @@
|
||||
"download": "Baixar Rótulo",
|
||||
"print": "Imprimir etiqueta",
|
||||
"server_print": "Imprimir no Servidor",
|
||||
"titles": "Etiquetas",
|
||||
"toast": {
|
||||
"load_status_failed": "Falha ao carregar status",
|
||||
"print_failed": "Falhou ao imprimir etiqueta",
|
||||
"print_success": "Etiqueta impressa"
|
||||
}
|
||||
"titles": "Etiquetas"
|
||||
},
|
||||
"page_qr_code": {
|
||||
"page_url": "URL da página",
|
||||
@@ -89,70 +62,34 @@
|
||||
}
|
||||
},
|
||||
"item": {
|
||||
"attachments_list": {
|
||||
"download": "Download",
|
||||
"open_new_tab": "Abrir em uma nova aba"
|
||||
},
|
||||
"create_modal": {
|
||||
"delete_photo": "Apagar foto",
|
||||
"item_description": "Descrição do Item",
|
||||
"item_name": "Nome do Item",
|
||||
"item_photo": "Foto do Item 📷",
|
||||
"item_quantity": "Quantidade de Itens",
|
||||
"parent_item": "Item Pai",
|
||||
"rotate_photo": "Girar imagem",
|
||||
"set_as_primary_photo": "Definir como { isPrimary, select, true {non-} false {} other {}} foto primária",
|
||||
"title": "Criar Item",
|
||||
"toast": {
|
||||
"already_creating": "Já está criando um item",
|
||||
"create_failed": "Não foi possível criar o item",
|
||||
"create_success": "Item criado",
|
||||
"failed_load_parent": "Falhou ao carregar item pai - por favor selecione manualmente",
|
||||
"no_canvas_support": "Seu navegador não suporta operações de tela",
|
||||
"please_select_location": "Por favor seleciona a localização",
|
||||
"rotate_failed": "Falha ao rotacionar imagem: { error }",
|
||||
"rotate_process_failed": "Falhou ao processar imagem rotacionada",
|
||||
"some_photos_failed": "{count, plural, =0 {Nenhuma foto para upload.} =1 {1 imagem falhou no upload.} other {Algumas fotos falharam durante upload.}}",
|
||||
"upload_failed": "Falhou ao carregar a imagem: { photoName }",
|
||||
"upload_success": "{count, plural, =0 {Nenhuma foto carregada.} =1 {Foto carregada com sucesso..} other {Todas as fotos carregadas com sucesso .}}",
|
||||
"uploading_photos": "{count, plural, =0 {Nenhuma foto para enviar.} =1 {Enviando 1 foto…} other {Enviando {count} fotos…}}"
|
||||
},
|
||||
"upload_photos": "Carregar Fotos",
|
||||
"uploaded": "Fotos enviadas"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Nenhum Resultado Encontrado",
|
||||
"placeholder": "Selecione…",
|
||||
"search_placeholder": "Digite para pesquisar…"
|
||||
"upload_photos": "Carregar Fotos"
|
||||
},
|
||||
"view": {
|
||||
"selectable": {
|
||||
"card": "Cartão",
|
||||
"items": "Items",
|
||||
"no_items": "Nenhum Item para Exibir",
|
||||
"no_items": "Nenhum item para exibir",
|
||||
"table": "Tabela"
|
||||
},
|
||||
"table": {
|
||||
"headers": "Cabeçalhos",
|
||||
"page": "Página",
|
||||
"rows_per_page": "Linhas por página",
|
||||
"table_settings": "Configurações da Tabela",
|
||||
"view_item": "Visualizar item"
|
||||
"table_settings": "Configurações da Tabela"
|
||||
}
|
||||
}
|
||||
},
|
||||
"label": {
|
||||
"create_modal": {
|
||||
"label_color": "Cor do Rótulo",
|
||||
"label_description": "Descrição da Texto",
|
||||
"label_description": "Descrição da Etiqueta",
|
||||
"label_name": "Nome da Etiqueta",
|
||||
"title": "Criar etiqueta",
|
||||
"toast": {
|
||||
"already_creating": "Etiqueta já criada",
|
||||
"create_failed": "Não foi possível criar etiqueta",
|
||||
"create_success": "Etiqueta criada",
|
||||
"label_name_too_long": "Nome da etiqueta não deve ultrapassar 50 caracteres"
|
||||
}
|
||||
"title": "Criar etiqueta"
|
||||
},
|
||||
"selector": {
|
||||
"select_labels": "Selecionar Etiquetas"
|
||||
@@ -162,12 +99,7 @@
|
||||
"create_modal": {
|
||||
"location_description": "Descrição do Local",
|
||||
"location_name": "Nome do Local",
|
||||
"title": "Criar Localização",
|
||||
"toast": {
|
||||
"already_creating": "Localização já foi criada",
|
||||
"create_failed": "Não foi possível criar a localização",
|
||||
"create_success": "Localização criada"
|
||||
}
|
||||
"title": "Criar Local"
|
||||
},
|
||||
"selector": {
|
||||
"no_location_found": "Nenhum local encontrado",
|
||||
@@ -192,11 +124,8 @@
|
||||
"confirm": "Confirmar",
|
||||
"create": "Criar",
|
||||
"create_and_add": "Criar e Adicionar Outro",
|
||||
"create_subitem": "Criar Subitem",
|
||||
"created": "Criado",
|
||||
"delete": "Excluir",
|
||||
"delete_confirm": "Tem certeza que deseja excluir este item? ",
|
||||
"demo_instance": "Esta é uma instancia de demonstração",
|
||||
"details": "Detalhes",
|
||||
"duplicate": "Duplicar",
|
||||
"edit": "Editar",
|
||||
@@ -211,7 +140,6 @@
|
||||
"items": "Items",
|
||||
"join_discord": "Junte-se ao Discord",
|
||||
"labels": "Etiquetas",
|
||||
"loading": "Carregando…",
|
||||
"locations": "Locais",
|
||||
"maintenance": "Manutenção",
|
||||
"name": "Nome",
|
||||
@@ -219,14 +147,11 @@
|
||||
"password": "Senha",
|
||||
"quantity": "Quantidade",
|
||||
"read_docs": "Leia a Documentação",
|
||||
"return_home": "Retornar para Home",
|
||||
"save": "Salvar",
|
||||
"search": "Buscar",
|
||||
"sign_out": "Encerrar sessão",
|
||||
"submit": "Enviar",
|
||||
"unknown": "Desconhecido",
|
||||
"update": "Atualizar",
|
||||
"updating": "Atualizando",
|
||||
"value": "Valor",
|
||||
"version": "Versão: { version }",
|
||||
"welcome": "Bem-vindo, {username}"
|
||||
@@ -251,49 +176,27 @@
|
||||
"set_email": "Qual o seu e-mail?",
|
||||
"set_name": "Qual é o seu nome?",
|
||||
"set_password": "Defina a sua Senha",
|
||||
"tagline": "Rastreie, organize e gerencie suas coisas.",
|
||||
"title": "Organize e Etiquete Suas Coisas",
|
||||
"toast": {
|
||||
"invalid_email": "Endereço de email inválido",
|
||||
"invalid_email_password": "Email ou senha inválidos",
|
||||
"login_success": "Logado com sucesso",
|
||||
"problem_registering": "Problema ao registrar usuário",
|
||||
"user_registered": "Usuário registrado"
|
||||
}
|
||||
"tagline": "Rastreie, organize e gerencie suas coisas."
|
||||
},
|
||||
"items": {
|
||||
"add": "Adicionar",
|
||||
"advanced": "Avançada",
|
||||
"archived": "Arquivado",
|
||||
"asset_id": "ID do Ativo",
|
||||
"associated_with_multiple": "Este ID de ativo está associado a vários itens",
|
||||
"attachment": "Anexo",
|
||||
"attachments": "Anexos",
|
||||
"changes_persisted_immediately": "Alterações nos anexos serão salvas imediatamente",
|
||||
"created_at": "Criado em",
|
||||
"custom_fields": "Campos Personalizados",
|
||||
"delete_attachment_confirm": "Tem certeza que deseja excluir este anexo?",
|
||||
"delete_item_confirm": "Tem certeza que deseja excluir este item?",
|
||||
"description": "Descrição",
|
||||
"details": "Detalhes",
|
||||
"drag_and_drop": "Arraste e solte os arquivos aqui ou clique para selecionar os arquivos",
|
||||
"edit": {
|
||||
"edit_attachment_dialog": {
|
||||
"attachment_title": "Título do Anexo",
|
||||
"attachment_type": "Tipo do Anexo",
|
||||
"primary_photo": "Foto Principal",
|
||||
"primary_photo_sub": "Esta opção está disponível apenas para fotos. Apenas uma foto pode ser a principal. Se você selecionar esta opção, a foto principal atual, se houver, será desmarcada.",
|
||||
"select_type": "Selecione um tipo",
|
||||
"title": "Editar Anexo"
|
||||
}
|
||||
},
|
||||
"edit_details": "Detalhes da Edição",
|
||||
"field_selector": "Seletor de Campo",
|
||||
"field_value": "Valor do Campo",
|
||||
"first": "Primeira",
|
||||
"include_archive": "Incluir itens arquivados",
|
||||
"insured": "Segurado",
|
||||
"invalid_asset_id": "ID de ativo inválido",
|
||||
"last": "Última",
|
||||
"lifetime_warranty": "Garantia Vitalícia",
|
||||
"location": "Local",
|
||||
@@ -304,7 +207,6 @@
|
||||
"name": "Nome",
|
||||
"negate_labels": "Negar Rótulos Selecionados",
|
||||
"next_page": "Próxima página",
|
||||
"no_attachments": "Nenhum anexo encontrado",
|
||||
"no_results": "Nenhum Item Encontrado",
|
||||
"notes": "Anotações",
|
||||
"only_with_photo": "Somente itens com foto",
|
||||
@@ -326,60 +228,24 @@
|
||||
"receipts": "Recibos",
|
||||
"reset_search": "Reiniciar Pesquisa",
|
||||
"results": "{ total } Resultados",
|
||||
"select_field": "Selecione o campo",
|
||||
"serial_number": "Número Serial",
|
||||
"show_advanced_view_options": "Exibir Opções de Visualização Avançada",
|
||||
"sold_at": "Vendido Em",
|
||||
"sold_details": "Detalhes da Venda",
|
||||
"sold_price": "Preço da Venda",
|
||||
"sold_to": "Vendido Para",
|
||||
"sync_child_locations": "Sincronizar a localização dos itens filhos",
|
||||
"tip_1": "Os filtros de local e etiqueta usam o operador \"OR\". Se você selecionar mais de um, somente um será\nnecessário para obter um resultado.",
|
||||
"tip_2": "As pesquisas comprefixo '#'' buscam um ID de ativo (exemplo '#000-001')",
|
||||
"tip_3": "Os filtros de local e etiqueta usam o operador \"OR\". Se você selecionar mais de um, somente um será \nnecessário para obter um resultado.",
|
||||
"tip_3": "Os filtros de local e etiqueta usam o operador \"OR\". Se você selecionar mais de um, somente um será\nnecessário para obter um resultado.",
|
||||
"tips": "Dicas",
|
||||
"tips_sub": "Dicas de pesquisa",
|
||||
"toast": {
|
||||
"asset_not_found": "Ativo não encontrado",
|
||||
"attachment_deleted": "Anexo apagado",
|
||||
"attachment_updated": "Anexo atualizado",
|
||||
"attachment_uploaded": "Anexo enviado",
|
||||
"child_items_location_no_longer_synced": "As localizações dos itens filhos não serão mais sincronizadas com este item.",
|
||||
"child_items_location_synced": "As localizações dos itens filhos foram sincronizadas com este item",
|
||||
"child_location_desync": "Alterar a localização irá dessincronizá-la da localização dos pais",
|
||||
"error_loading_parent_data": "Algo deu errado ao tentar carregar os dados dos pais",
|
||||
"failed_adjust_quantity": "Falha ao ajustar quantidade",
|
||||
"failed_delete_attachment": "Falhou ao apagar anexo",
|
||||
"failed_delete_item": "Falhou ao apagar item",
|
||||
"failed_duplicate_item": "Falhou ao duplicar item",
|
||||
"failed_load_asset": "Falha ao carregar o ativo",
|
||||
"failed_load_item": "Falha ao carregar item",
|
||||
"failed_load_items": "Falha ao carregar o ativos",
|
||||
"failed_save": "Falha ao salvar item",
|
||||
"failed_save_no_location": "Falha ao salvar item: localização não selecionada",
|
||||
"failed_search_items": "Falha ao localizar itens",
|
||||
"failed_update_attachment": "Falha ao atualizar anexo",
|
||||
"failed_upload_attachment": "Falha ao enviar anexo",
|
||||
"item_deleted": "Item apagado",
|
||||
"item_saved": "Item salvo",
|
||||
"quantity_cannot_negative": "Quantidade não pode ser negativa",
|
||||
"sync_child_location": "O item pai selecionado sincroniza a localização dos filhos com a sua. A localização foi atualizada."
|
||||
},
|
||||
"updated_at": "Atualizado em",
|
||||
"warranty": "Garantia",
|
||||
"warranty_details": "Detalhes da Garantia",
|
||||
"warranty_expires": "Garantia Expira Em"
|
||||
},
|
||||
"labels": {
|
||||
"label_delete_confirm": "Tem certeza de que deseja excluir este marcador? Esta ação não pode ser desfeita.",
|
||||
"no_results": "Nenhuma etiqueta encontrada",
|
||||
"toast": {
|
||||
"failed_delete_label": "Falha ao deletar etiqueta",
|
||||
"failed_load_label": "Falha ao deletar rótulo",
|
||||
"failed_update_label": "Falha ao deletar rótulo",
|
||||
"label_deleted": "Rótulo excluído",
|
||||
"label_updated": "Rótulo atualizado"
|
||||
},
|
||||
"update_label": "Atualizar Etiqueta"
|
||||
},
|
||||
"languages": {
|
||||
@@ -395,9 +261,7 @@
|
||||
"it": "Italiano",
|
||||
"ja-JP": "Japonês",
|
||||
"ko-KR": "Coreano",
|
||||
"lb-LU": "Luxemburguês (Luxemburgo)",
|
||||
"lt-LT": "Lituano (Lituânia)",
|
||||
"nb-NO": "Dano norueguês",
|
||||
"nb-NO": "Danonorueguês",
|
||||
"nl": "Holandês",
|
||||
"pl": "Polonês",
|
||||
"pt-BR": "Português (Brasil)",
|
||||
@@ -422,16 +286,8 @@
|
||||
"child_locations": "Locais Filhos",
|
||||
"collapse_tree": "Ocultar Árvore",
|
||||
"expand_tree": "Expandir Árvore",
|
||||
"location_items_delete_confirm": "Tem certeza de que deseja excluir este local e todos os seus itens? Esta ação não pode ser desfeita.",
|
||||
"no_results": "Nenhum local encontrado",
|
||||
"toast": {
|
||||
"failed_delete_location": "Falhou ao apagar localização",
|
||||
"failed_load_location": "Falhou ao carregar localização",
|
||||
"failed_update_location": "Falha ao atualizar localização",
|
||||
"location_deleted": "Localização deletada",
|
||||
"location_updated": "Localização atualizada"
|
||||
},
|
||||
"update_location": "Atualizar Localização"
|
||||
"update_location": "Atualizar Local"
|
||||
},
|
||||
"maintenance": {
|
||||
"filter": {
|
||||
@@ -486,16 +342,13 @@
|
||||
"currency_format": "Formato da moeda",
|
||||
"current_password": "Senha Atual",
|
||||
"delete_account": "Excluir conta",
|
||||
"delete_account_confirm": "Tem certeza de que deseja excluir sua conta? Se você for o último membro do grupo, todos os seus dados serão excluídos. Esta ação não pode ser desfeita.",
|
||||
"delete_account_sub": "Excluir sua conta e todos os dados associados. Essa ação não pode ser desfeita.",
|
||||
"delete_notifier_confirm": "Tem certeza que deseja apagar o notificador?",
|
||||
"display_legacy_header": "{ currentValue, select, true {Desativar cabeçalho legado} false {Ativar cabeçalho legado} other {Não atingido}}",
|
||||
"enabled": "Ativado",
|
||||
"example": "Exemplo",
|
||||
"gen_invite": "Gerar link de convite",
|
||||
"group_settings": "Definições do grupo",
|
||||
"group_settings_sub": "Configurações de Grupo Compartilhado. É possível que tenha que recarregar a página para que alguns ajustes sejam aplicados.",
|
||||
"inactive": "Inativo",
|
||||
"inactive": "Inativa",
|
||||
"language": "Idioma",
|
||||
"new_password": "Nova Senha",
|
||||
"no_notifiers": "Nenhum notificador configurado",
|
||||
@@ -507,62 +360,16 @@
|
||||
"test": "Teste",
|
||||
"theme_settings": "Configurações do Tema",
|
||||
"theme_settings_sub": "As configurações de tema são salvas localmente em seu navegador. Você pode mudar o tema a qualquer momento. Se está\ntendo problemas com a mudança de tema, tente recarregar a pagina.",
|
||||
"toast": {
|
||||
"account_deleted": "Sua conta foi apagada.",
|
||||
"failed_change_password": "Falha ao alterar senha.",
|
||||
"failed_create_notifier": "Falha ao criar notificação.",
|
||||
"failed_delete_account": "Falha ao apagar usa conta.",
|
||||
"failed_delete_notifier": "Falha ao apagar notificador.",
|
||||
"failed_get_currencies": "Falha ao obter cotações",
|
||||
"failed_test_notifier": "Falha ao testar notificador.",
|
||||
"failed_update_group": "Falha ao atualizar grupo",
|
||||
"failed_update_notifier": "Falha ao atualizar o notificador.",
|
||||
"group_updated": "Grupo atualizado",
|
||||
"notifier_test_success": "Notificador testado com sucesso.",
|
||||
"password_changed": "Senha alterada com sucesso."
|
||||
},
|
||||
"update_group": "Atualizar grupo",
|
||||
"update_language": "Atualizar idioma",
|
||||
"url": "URL",
|
||||
"user_profile": "Perfil do usuário",
|
||||
"user_profile_sub": "Convide usuários e gerencie sua conta."
|
||||
},
|
||||
"reports": {
|
||||
"label_generator": {
|
||||
"asset_end": "Fim do Ativo",
|
||||
"asset_start": "Inicio do Ativo",
|
||||
"base_url": "URL Base",
|
||||
"bordered_labels": "Etiquetas com bordas",
|
||||
"generate_page": "Gerando Página",
|
||||
"input_placeholder": "Digite aqui",
|
||||
"instruction_1": "O Gerador de Etiquetas Homebox é uma ferramenta para ajudar você a imprimir etiquetas para o seu inventário Homebox. Elas são projetadas para\nserem etiquetas de impressão antecipada, para que você possa imprimir várias etiquetas e tê-las prontas para aplicar",
|
||||
"instruction_2": "Dessa forma, essas etiquetas funcionam imprimindo um código QR de URL e informações de AssetID em uma etiqueta. Se você desativou\nos AssetIDs nas configurações do seu Homebox, ainda poderá usar esta ferramenta, mas os AssetIDs não farão referência a nenhum item",
|
||||
"instruction_3": "Este recurso está em estágios iniciais de desenvolvimento e pode mudar em versões futuras. Se você tiver algum feedback,\nfaça-o na '<a href=\"https://github.com/sysadminsmedia/homebox/discussions/53\">'Discussão do GitHub'</a>'",
|
||||
"label_height": "Altura da Etiqueta",
|
||||
"label_width": "Largura da Etiqueta",
|
||||
"measure_type": "Tipo da Medida",
|
||||
"page_bottom_padding": "Espaçamento inferior da página",
|
||||
"page_height": "Altura da Página",
|
||||
"page_left_padding": "Espaçamento Esquerdo da Página",
|
||||
"page_right_padding": "Espaçamento Direito da Página",
|
||||
"page_top_padding": "Espaçamento Superior da Página",
|
||||
"page_width": "Largura da Página",
|
||||
"qr_code_example": "Exemplo de QR Code",
|
||||
"tip_1": "Os padrões aqui são configurados para as\n'<a href=\"https://www.avery.com/templates/5260\">'Folhas de etiquetas Avery 5260'</a>'. Se estiver usando uma folha diferente,\nserá necessário ajustar as configurações para corresponder à sua folha.",
|
||||
"tip_2": "Se você estiver personalizando sua planilha, as dimensões estão em polegadas. Ao criar a planilha 5260, descobri que as\ndimensões usadas no modelo não correspondiam ao que era necessário para imprimir dentro das caixas.\n'<b>'Esteja preparado para algumas tentativas e erros.'</b>'",
|
||||
"tip_3": "Ao imprimir, certifique-se de:\n'<ol><li>'Definir as margens como 0 ou Nenhuma'</li><li>'Definir a escala como 100%'</li><li>'Desativar a impressão frente e verso'</li><li>'Imprimir uma página de teste antes de imprimir várias páginas'</li></ol>'",
|
||||
"tips": "Dicas",
|
||||
"title": "Gerador de Etiquetas",
|
||||
"toast": {
|
||||
"page_too_small_card": "Tamanho da página é muito pequena para o tamanho do cartão"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
"error": "Ocorreu um erro durante a digitalização",
|
||||
"invalid_url": "Código de barras inválido",
|
||||
"no_sources": "Nenhuma fonte de vídeo disponível",
|
||||
"permission_denied": "Permissão de câmera negada, permita o acesso à câmera nas configurações do seu navegador",
|
||||
"select_video_source": "Escolha uma fonte de vídeo",
|
||||
"title": "Scanner",
|
||||
"unsupported": "A API do Fluxo de Mídia não é suportada sem o protocolo HTTPS"
|
||||
@@ -570,24 +377,17 @@
|
||||
"tools": {
|
||||
"actions": "Ações de inventário",
|
||||
"actions_set": {
|
||||
"create_missing_thumbnails": "Criar Miniaturas Ausentes",
|
||||
"create_missing_thumbnails_button": "Miniatura Criada",
|
||||
"create_missing_thumbnails_confirm": "Tem certeza de que deseja criar as miniaturas ausentes? Isso pode demorar um pouco e não pode ser pausado.",
|
||||
"create_missing_thumbnails_sub": "Cria miniaturas para todos os anexos suportados pela configuração atual. Isso é útil para anexos enviados antes da versão v0.20.0 do Homebox. Isso não substituirá as miniaturas existentes, apenas criará novas para anexos que não tenham miniatura. Observe que as miniaturas são criadas em segundo plano e podem levar algum tempo para serem concluídas.",
|
||||
"ensure_ids": "Garantir IDs de ativos",
|
||||
"ensure_ids_button": "Garantir IDs de ativos",
|
||||
"ensure_ids_confirm": "Tem certeza de que deseja garantir que todos os ativos tenham um ID? Isso pode demorar um pouco e não pode ser desfeito.",
|
||||
"ensure_ids_sub": "Garante que todos os itens em seu inventário tenham um campo de asset_id válido. Isso é feito através da procura do mais alto asset_id no banco de dados e aplicando o próximo valor aos itens seguintes que não tenham valores de asset_id definidos. Isso é feito com base no campo created_at.",
|
||||
"ensure_import_refs": "Garantir refs de importação",
|
||||
"ensure_import_refs_button": "Garantir refs de importação",
|
||||
"ensure_import_refs_sub": "Garante que todos os itens no seu inventário tenham um campo import_ref válido. Isso é feito gerando aleatoriamente uma sequência de 8 caracteres para cada item que não possuir um campo import_ref válido.",
|
||||
"set_primary_photo": "Definir foto principal",
|
||||
"set_primary_photo_button": "Definir foto principal",
|
||||
"set_primary_photo_confirm": "Tem certeza de que deseja definir as fotos principais? Isso pode demorar um pouco e não pode ser desfeito.",
|
||||
"set_primary_photo_sub": "Na versão v0.10.0 do Homebox, o campo principal de foto foi adicionado aos anexos do tipo foto. Essa ação irá marcar o campo da imagem principal para a primeira imagem no array de anexos do banco de dados, se não estiver feito ainda.'<a class=\"link\" href=\"https://github.com/hay-kot/homebox/pull/576\">'Veja no GitHub PR #576'</a>'",
|
||||
"zero_datetimes": "Zerar Data Hora do Item",
|
||||
"zero_datetimes_button": "Zerar Data Hora do Item",
|
||||
"zero_datetimes_confirm": "Tem certeza de que deseja redefinir todos os valores de data e hora? Isso pode demorar um pouco e não pode ser desfeito.",
|
||||
"zero_datetimes_sub": "Redefine o valor de hora de todos os itens do inventário para o início da data. Isso é para corrigir um bug que foi introduzido no início do desenvolvimento do site que causava o valor da hora ser armazenado errado, resultando em um erro na mostra da data/hora do item. '<a class=\"link\" href=\"https://github.com/hay-kot/homebox/issues/236\" target=\"_blank\">'Veja o item #236 do Github para mais detalhes.'</a>'"
|
||||
},
|
||||
"actions_sub": "Aplicar ações ao seu inventário em massa. Essas ações são irreversíveis. '<b>'Tenha cuidado'</b>'",
|
||||
@@ -598,7 +398,6 @@
|
||||
"export_sub": "Exporta o formato CSV padrão para o Homebox. Isso irá exportar todos os itens do seu inventário.",
|
||||
"import": "Importar inventário",
|
||||
"import_button": "Importar inventário",
|
||||
"import_ref_confirm": "Tem certeza de que deseja garantir que todos os ativos tenham um import_ref? Isso pode demorar um pouco e não pode ser desfeito.",
|
||||
"import_sub": "Importa o formato CSV padrão para o Homebox. Sem uma coluna '<code>'HB.import_ref'</code>' , isso '<b>não</b>' vai sobrescrever nenhum item de seu inventário, apenas adicionar novos items. Linhas que tenham '<code>'HB.import_ref'</code>' colunas são mescladas em itens existentes com o mesmo import_ref, se existir."
|
||||
},
|
||||
"import_export_sub": "Importar e exportar seu inventário de ou para um arquivo CSV. Isso é útil para migrar o seu inventário para uma nova instância do Homebox.",
|
||||
@@ -611,14 +410,6 @@
|
||||
"bill_of_materials_button": "Gerar Lista de Materiais",
|
||||
"bill_of_materials_sub": "Gera um arquivo CSV (Valores Separados por Vírgula) que pode ser importado para um programa de planilha. Esse é um sumário do seu inventário com informações básicas dos itens e dos preços."
|
||||
},
|
||||
"reports_sub": "Gere relatórios diferentes para o seu inventário.",
|
||||
"toast": {
|
||||
"asset_success": "{ results } os ativos foram atualizados.",
|
||||
"failed_create_missing_thumbnails": "Falha ao criar miniaturas faltantes.",
|
||||
"failed_ensure_ids": "Falha ao garantir IDs de ativos.",
|
||||
"failed_ensure_import_refs": "Falha ao garantir referências de importação.",
|
||||
"failed_set_primary_photos": "Falha ao definir foto principal.",
|
||||
"failed_zero_datetimes": "Falha ao reiniciar os valores de data e hora."
|
||||
}
|
||||
"reports_sub": "Gere relatórios diferentes para o seu inventário."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,16 +113,10 @@
|
||||
"upload_photos": "Carregar Fotos",
|
||||
"uploaded": "Foto Carregada"
|
||||
},
|
||||
"product_import": {
|
||||
"barcode": "Código de Barras do Produto",
|
||||
"error_not_found": "Nenhum produto encontrado com código de barras.",
|
||||
"search_item": "Pesquisar produto",
|
||||
"title": "Importar produto"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Nenhum Resultado Encontrado",
|
||||
"placeholder": "Selecionar…",
|
||||
"search_placeholder": "Escreva para pesquisar…"
|
||||
"placeholder": "Selecionar...",
|
||||
"search_placeholder": "Escreva para pesquisar..."
|
||||
},
|
||||
"view": {
|
||||
"selectable": {
|
||||
@@ -135,8 +129,7 @@
|
||||
"headers": "Cabeçalhos",
|
||||
"page": "Página",
|
||||
"rows_per_page": "Linhas por página",
|
||||
"table_settings": "Definições da Tabela",
|
||||
"view_item": "Ver item"
|
||||
"table_settings": "Definições da Tabela"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -174,7 +167,7 @@
|
||||
"select_location": "Selecionar uma Localização"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "Sem localizações disponíveis. Adicione novas localizações através do botão \n'<span class=\"link-primary\">'Criar'</span>' na barra de navegação."
|
||||
"no_locations": "Sem localizações disponíveis. Adicione novas localizações através do botão \n`<`span class=\"link-primary\"`>`Criar`<`/span`>` na barra de navegação."
|
||||
}
|
||||
},
|
||||
"quick_menu": {
|
||||
@@ -209,7 +202,7 @@
|
||||
"items": "Itens",
|
||||
"join_discord": "Junte-se ao Discord",
|
||||
"labels": "Etiquetas",
|
||||
"loading": "A Carregar…",
|
||||
"loading": "A Carregar...",
|
||||
"locations": "Localizações",
|
||||
"maintenance": "Manutenção",
|
||||
"name": "Nome",
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
{
|
||||
"components": {
|
||||
"app": {
|
||||
"create_modal": {
|
||||
"createAndAddAnother": "Нажмите {shiftKey} + {enterKey}, чтобы создать и добавить ещё один.",
|
||||
"enter": "Создать",
|
||||
"shift": "Shift"
|
||||
},
|
||||
"import_dialog": {
|
||||
"change_warning": "Изменено поведение для импортов с существующими import_ref. Если в CSV-файле присутствует import_ref, \nто элемент будет обновлен значениями из CSV-файла.",
|
||||
"description": "Импортируйте CSV-файл, содержащий ваши предметы, ярлыки и местоположения. Для получения информации о требуемом формате \nсм. документацию.",
|
||||
"title": "Импорт CSV файла",
|
||||
"toast": {
|
||||
"import_failed": "Импорт не удался. Повторите попытку позже.",
|
||||
"import_success": "Импорт выполнен успешно!",
|
||||
"please_select_file": "Выберите файл для импорта."
|
||||
}
|
||||
"title": "Импорт CSV файла"
|
||||
},
|
||||
"outdated": {
|
||||
"current_version": "Текущая версия",
|
||||
@@ -24,18 +14,6 @@
|
||||
"new_version_available_link": "Показать комментарии к версии"
|
||||
}
|
||||
},
|
||||
"color_selector": {
|
||||
"clear": "Очистить цвет",
|
||||
"color": "Цвет",
|
||||
"no_color": "Без цвета",
|
||||
"no_color_selected": "Цвет не выбран",
|
||||
"randomize": "Случайный цвет"
|
||||
},
|
||||
"form": {
|
||||
"password": {
|
||||
"toggle_show": "Показать/скрыть пароль"
|
||||
}
|
||||
},
|
||||
"global": {
|
||||
"copy_text": {
|
||||
"documentation": "документация",
|
||||
@@ -67,74 +45,18 @@
|
||||
"years": "лет",
|
||||
"yesterday": "вчера"
|
||||
},
|
||||
"label_maker": {
|
||||
"browser_print": "Печать из браузера",
|
||||
"confirm_description": "Вы уверены, что хотите напечатать эту этикетку?",
|
||||
"download": "Скачать этикетку",
|
||||
"print": "Напечатать этикетку",
|
||||
"server_print": "Печать на сервере",
|
||||
"titles": "Этикетки",
|
||||
"toast": {
|
||||
"load_status_failed": "Не удалось загрузить статус",
|
||||
"print_failed": "Не удалось напечатать этикетку",
|
||||
"print_success": "Этикетка напечатана"
|
||||
}
|
||||
},
|
||||
"page_qr_code": {
|
||||
"page_url": "URL-адрес страницы",
|
||||
"qr_tooltip": "Показать QR-код"
|
||||
"page_url": "URL-адрес страницы"
|
||||
},
|
||||
"password_score": {
|
||||
"password_strength": "Сложность пароля"
|
||||
}
|
||||
},
|
||||
"item": {
|
||||
"attachments_list": {
|
||||
"download": "Скачать",
|
||||
"open_new_tab": "Открыть в новой вкладке"
|
||||
},
|
||||
"create_modal": {
|
||||
"delete_photo": "Удалить фото",
|
||||
"item_description": "Описание элемента",
|
||||
"item_name": "Имя элемента",
|
||||
"item_photo": "Фото элемента 📷",
|
||||
"item_quantity": "Количество",
|
||||
"parent_item": "Родительский элемент",
|
||||
"product_tooltip_input_barcode": "Автозаполнение по введённому штрих-коду",
|
||||
"product_tooltip_scan_barcode": "Автозаполнение по штрих-коду с камеры 📷",
|
||||
"rotate_photo": "Повернуть фото",
|
||||
"set_as_primary_photo": "Сделать {isPrimary, select, true {не} false {} other {}}основным фото",
|
||||
"title": "Создать элемент",
|
||||
"toast": {
|
||||
"already_creating": "Элемент уже создаётся",
|
||||
"create_failed": "Не удалось создать элемент",
|
||||
"create_success": "Элемент создан",
|
||||
"failed_load_parent": "Не удалось загрузить родительский элемент — выберите вручную",
|
||||
"no_canvas_support": "Ваш браузер не поддерживает операции с canvas",
|
||||
"please_select_location": "Выберите местоположение.",
|
||||
"rotate_failed": "Не удалось повернуть изображение: { error }",
|
||||
"rotate_process_failed": "Не удалось обработать повёрнутое изображение",
|
||||
"some_photos_failed": "{count, plural, =0 {Нет фотографий для загрузки.} =1 {Не удалось загрузить 1 фотографию.} other {Не удалось загрузить некоторые фотографии.}}",
|
||||
"upload_failed": "Не удалось загрузить фотографию: { photoName }",
|
||||
"upload_success": "{count, plural, =0 {Фотографии не загружены.} =1 {Фотография успешно загружена.} other {Все фотографии успешно загружены.}}",
|
||||
"uploading_photos": "{count, plural, =0 {Нет фотографий для загрузки} =1 {Загружается 1 фотография…} other {Загружается {count} фотографий…}}"
|
||||
},
|
||||
"upload_photos": "Загрузить фотографии",
|
||||
"uploaded": "Загруженное фото"
|
||||
},
|
||||
"product_import": {
|
||||
"barcode": "Штрих-код продукта",
|
||||
"db_source": "Источник БД",
|
||||
"error_exception": "Ошибка при получении штрих-кода элемента: ",
|
||||
"error_invalid_barcode": "Указан недействительный штрих-код",
|
||||
"error_not_found": "Продукт с данным штрих-кодом не найден.",
|
||||
"search_item": "Найти продукт",
|
||||
"title": "Импорт продукта"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Результаты не найдены",
|
||||
"placeholder": "Выберите…",
|
||||
"search_placeholder": "Введите для поиска…"
|
||||
"title": "Создать элемент"
|
||||
},
|
||||
"view": {
|
||||
"selectable": {
|
||||
@@ -144,103 +66,62 @@
|
||||
"table": "Таблица"
|
||||
},
|
||||
"table": {
|
||||
"headers": "Заголовки",
|
||||
"page": "Страница",
|
||||
"rows_per_page": "Строк на странице",
|
||||
"table_settings": "Настройки таблицы",
|
||||
"view_item": "Просмотр элемента"
|
||||
"rows_per_page": "Строк на странице"
|
||||
}
|
||||
}
|
||||
},
|
||||
"label": {
|
||||
"create_modal": {
|
||||
"label_color": "Цвет метки",
|
||||
"label_description": "Описание метки",
|
||||
"label_name": "Название метки",
|
||||
"title": "Создать метку",
|
||||
"toast": {
|
||||
"already_creating": "Метка уже создаётся",
|
||||
"create_failed": "Не удалось создать метку",
|
||||
"create_success": "Метка создана",
|
||||
"label_name_too_long": "Название метки не должно превышать 50 символов"
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"select_labels": "Выбрать метки"
|
||||
"title": "Создать метку"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"create_modal": {
|
||||
"location_description": "Описание места хранения",
|
||||
"location_name": "Название места хранения",
|
||||
"title": "Создать локацию",
|
||||
"toast": {
|
||||
"already_creating": "Локация уже создаётся",
|
||||
"create_failed": "Не удалось создать локацию",
|
||||
"create_success": "Локация создана"
|
||||
}
|
||||
"title": "Создать локацию"
|
||||
},
|
||||
"selector": {
|
||||
"no_location_found": "Локация не найдена",
|
||||
"parent_location": "Родительская локация",
|
||||
"search_location": "Поиск локаций",
|
||||
"select_location": "Выберите локацию"
|
||||
"parent_location": "Родительская локация"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "Нет доступных локаций. Добавьте новую локацию,\nнажав на кнопку '<span class=\"link-primary\">Создать</span>' в навигационном меню."
|
||||
"no_locations": "Нет доступных локаций. Добавьте новую локацию, \nнажав на кнопку `<`span class=\"link-primary\"`>`Создать`<`/span`>` в навигационном меню."
|
||||
}
|
||||
},
|
||||
"quick_menu": {
|
||||
"no_results": "Результаты не найдены.",
|
||||
"shortcut_hint": "Используйте цифровые клавиши, чтобы быстро выбрать действие."
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"api_failure": "Ошибка вызова API сервера: "
|
||||
},
|
||||
"global": {
|
||||
"add": "Добавить",
|
||||
"archived": "Архивный",
|
||||
"build": "Сборка: { build }",
|
||||
"cancel": "Отмена",
|
||||
"confirm": "Подтвердить",
|
||||
"create": "Создать",
|
||||
"create_and_add": "Создать и добавить еще",
|
||||
"create_subitem": "Создать вложенный элемент",
|
||||
"created": "Создано",
|
||||
"delete": "Удалить",
|
||||
"delete_confirm": "Вы уверены, что хотите удалить этот элемент? ",
|
||||
"demo_instance": "Это демонстрационный экземпляр",
|
||||
"details": "Подробнее",
|
||||
"duplicate": "Дублировать",
|
||||
"edit": "Редактировать",
|
||||
"email": "Email",
|
||||
"follow_dev": "Следить за разработчиком",
|
||||
"footer": {
|
||||
"api_link": "'<a href=\"https://homebox.software/en/api/\" target=\"_blank\">'API'</a>'",
|
||||
"version_link": "'<'a href=\"https://github.com/sysadminsmedia/homebox/releases/tag/{ version }\" target=\"_blank\"'>' Версия: { version } Сборка: { build } '</a>'"
|
||||
},
|
||||
"github": "Github проект",
|
||||
"insured": "Застрахован",
|
||||
"items": "Элементы",
|
||||
"join_discord": "Присоединяйтесь к Discord",
|
||||
"labels": "Метки",
|
||||
"loading": "Загрузка…",
|
||||
"locations": "Места",
|
||||
"maintenance": "Техническое обслуживание и ремонт",
|
||||
"name": "Имя",
|
||||
"navigate": "Навигация",
|
||||
"password": "Пароль",
|
||||
"quantity": "Количество",
|
||||
"read_docs": "Прочитать документацию",
|
||||
"return_home": "Вернуться домой",
|
||||
"save": "Сохранить",
|
||||
"search": "Поиск",
|
||||
"sign_out": "Выйти",
|
||||
"submit": "Отправить",
|
||||
"unknown": "Неизвестно",
|
||||
"update": "Обновить",
|
||||
"updating": "Обновление",
|
||||
"value": "Значение",
|
||||
"version": "Версия: { version }",
|
||||
"welcome": "Добро пожаловать, { username }"
|
||||
@@ -265,49 +146,27 @@
|
||||
"set_email": "Какой у вас адрес электронной почты?",
|
||||
"set_name": "Как вас зовут?",
|
||||
"set_password": "Установите пароль",
|
||||
"tagline": "Отслеживайте, упорядочивайте и управляйте своими вещами.",
|
||||
"title": "Организуйте и отметьте свои вещи",
|
||||
"toast": {
|
||||
"invalid_email": "Неверный адрес электронной почты",
|
||||
"invalid_email_password": "Неверный email или пароль",
|
||||
"login_success": "Вход выполнен успешно",
|
||||
"problem_registering": "Проблема при регистрации пользователя",
|
||||
"user_registered": "Пользователь зарегистрирован"
|
||||
}
|
||||
"tagline": "Отслеживайте, упорядочивайте и управляйте своими вещами."
|
||||
},
|
||||
"items": {
|
||||
"add": "Добавить",
|
||||
"advanced": "Дополнительно",
|
||||
"archived": "В архиве",
|
||||
"asset_id": "Инвентарный ID",
|
||||
"associated_with_multiple": "Этот инвентарный номер связан с несколькими элементами",
|
||||
"attachment": "Вложение",
|
||||
"attachments": "Вложения",
|
||||
"changes_persisted_immediately": "Изменения во вложениях будут сохранены сразу же",
|
||||
"created_at": "Создано в",
|
||||
"custom_fields": "Настраиваемые поля",
|
||||
"delete_attachment_confirm": "Вы уверены, что хотите удалить это вложение?",
|
||||
"delete_item_confirm": "Вы уверены, что хотите удалить этот элемент?",
|
||||
"description": "Описание",
|
||||
"details": "Подробнее",
|
||||
"drag_and_drop": "Перетащите файлы сюда или нажмите, чтобы выбрать файлы",
|
||||
"edit": {
|
||||
"edit_attachment_dialog": {
|
||||
"attachment_title": "Название вложения",
|
||||
"attachment_type": "Тип вложения",
|
||||
"primary_photo": "Основное фото",
|
||||
"primary_photo_sub": "Эта функция доступна только для фотографий. Только одно фото может быть основным. Если вы выберете эту опцию, текущее основное фото, если таковое имеется, будет отменено.",
|
||||
"select_type": "Выберите тип",
|
||||
"title": "Редактирование вложения"
|
||||
}
|
||||
},
|
||||
"edit_details": "Редактирование деталей",
|
||||
"field_selector": "Поле выбора",
|
||||
"field_value": "Значение поля",
|
||||
"first": "Первый",
|
||||
"include_archive": "Включая архивированные элементы",
|
||||
"insured": "Застраховано",
|
||||
"invalid_asset_id": "Недействительный инвентарный номер",
|
||||
"last": "Последний",
|
||||
"lifetime_warranty": "Гарантия на весь срок эксплуатации",
|
||||
"location": "Местоположение",
|
||||
@@ -318,7 +177,6 @@
|
||||
"name": "Название",
|
||||
"negate_labels": "Снять выбранные ярлыки",
|
||||
"next_page": "Следующая страница",
|
||||
"no_attachments": "Вложения не найдены",
|
||||
"no_results": "Элементы не найдены",
|
||||
"notes": "Заметки",
|
||||
"only_with_photo": "Только элементы с фото",
|
||||
@@ -340,78 +198,35 @@
|
||||
"receipts": "Квитанции",
|
||||
"reset_search": "Сбросить поиск",
|
||||
"results": "{ total } Результатов",
|
||||
"select_field": "Выберите поле",
|
||||
"serial_number": "Серийный номер",
|
||||
"show_advanced_view_options": "Показать дополнительные параметры",
|
||||
"sold_at": "Место продажи",
|
||||
"sold_details": "Детали продажи",
|
||||
"sold_price": "Цена продажи",
|
||||
"sold_to": "Покупатель",
|
||||
"sync_child_locations": "Синхронизировать местоположения дочерних элементов",
|
||||
"tip_1": "При фильтрации по локации и по ярлыкам используется логический оператор «ИЛИ». Если выбрано несколько фильтров, то для срабатывания\n требуется лишь одно совпадение.",
|
||||
"tip_2": "Поисковые запросы с префиксом \"#\" должны включать в себя ID актива (прим. '#000-001')",
|
||||
"tip_3": "Фильтры по полю используют операцию «ИЛИ». Если выбрано несколько фильтров, для совпадения\n требуется только один.",
|
||||
"tips": "Подсказки",
|
||||
"tips_sub": "Поисковые подсказки",
|
||||
"toast": {
|
||||
"asset_not_found": "Элемент не найден",
|
||||
"attachment_deleted": "Вложение удалено",
|
||||
"attachment_updated": "Вложение обновлено",
|
||||
"attachment_uploaded": "Вложение загружено",
|
||||
"child_items_location_no_longer_synced": "Местоположения дочерних элементов больше не будут синхронизироваться с этим элементом.",
|
||||
"child_items_location_synced": "Местоположения дочерних элементов синхронизированы с этим элементом",
|
||||
"child_location_desync": "Изменение местоположения отключит синхронизацию с местоположением родительского элемента",
|
||||
"error_loading_parent_data": "Что-то пошло не так при попытке загрузить данные родительского элемента",
|
||||
"failed_adjust_quantity": "Не удалось изменить количество",
|
||||
"failed_delete_attachment": "Не удалось удалить вложение",
|
||||
"failed_delete_item": "Не удалось удалить элемент",
|
||||
"failed_duplicate_item": "Не удалось дублировать элемент",
|
||||
"failed_load_asset": "Не удалось загрузить актив",
|
||||
"failed_load_item": "Не удалось загрузить элемент",
|
||||
"failed_load_items": "Не удалось загрузить элементы",
|
||||
"failed_save": "Не удалось сохранить элемент",
|
||||
"failed_save_no_location": "Не удалось сохранить элемент: местоположение не выбрано",
|
||||
"failed_search_items": "Не удалось найти элементы",
|
||||
"failed_update_attachment": "Не удалось обновить вложение",
|
||||
"failed_upload_attachment": "Не удалось загрузить вложение",
|
||||
"item_deleted": "Элемент удалён",
|
||||
"item_saved": "Элемент сохранён",
|
||||
"quantity_cannot_negative": "Количество не может быть отрицательным",
|
||||
"sync_child_location": "Выбранный родительский элемент синхронизирует местоположения своих дочерних элементов со своим собственным. Местоположение было обновлено."
|
||||
},
|
||||
"updated_at": "Обновлено в",
|
||||
"warranty": "Гарантия",
|
||||
"warranty_details": "Гарантийная информация",
|
||||
"warranty_expires": "Срок гарантии истекает"
|
||||
},
|
||||
"labels": {
|
||||
"label_delete_confirm": "Вы уверены, что хотите удалить эту метку? Это действие нельзя отменить.",
|
||||
"no_results": "Метки не найдены",
|
||||
"toast": {
|
||||
"failed_delete_label": "Не удалось удалить метку",
|
||||
"failed_load_label": "Не удалось загрузить метку",
|
||||
"failed_update_label": "Не удалось обновить метку",
|
||||
"label_deleted": "Метка удалена",
|
||||
"label_updated": "Метка обновлена"
|
||||
},
|
||||
"update_label": "Обновить метку"
|
||||
},
|
||||
"languages": {
|
||||
"ca": "Каталанский",
|
||||
"cs-CZ": "Чешский",
|
||||
"de": "Немецкий",
|
||||
"en": "Английский",
|
||||
"es": "Испанский",
|
||||
"fi-FI": "Финский",
|
||||
"fr": "французский",
|
||||
"hu": "Венгерский",
|
||||
"id-ID": "Индонезийский",
|
||||
"it": "Итальянский",
|
||||
"ja-JP": "Японский",
|
||||
"ko-KR": "Корейский",
|
||||
"lb-LU": "Люксембургский (Люксембург)",
|
||||
"lt-LT": "Литовский (Литва)",
|
||||
"nb-NO": "Норвежский букмол",
|
||||
"nl": "Голландский",
|
||||
"pl": "Польский",
|
||||
"pt-BR": "Португальский (Бразилия)",
|
||||
@@ -419,8 +234,6 @@
|
||||
"ru": "Русский",
|
||||
"sl": "Словенский",
|
||||
"sv": "Шведский",
|
||||
"ta-IN": "Тамильский",
|
||||
"th-TH": "Тайский",
|
||||
"tr": "Турецкий",
|
||||
"uk-UA": "Украинский",
|
||||
"zh-CN": "Китайский (упрощенный)",
|
||||
@@ -435,16 +248,7 @@
|
||||
"locations": {
|
||||
"child_locations": "Вложенные локации",
|
||||
"collapse_tree": "Свернуть всё дерево",
|
||||
"expand_tree": "Развернуть дерево",
|
||||
"location_items_delete_confirm": "Вы уверены, что хотите удалить это местоположение и все его элементы? Это действие нельзя отменить.",
|
||||
"no_results": "Локаций не найдено",
|
||||
"toast": {
|
||||
"failed_delete_location": "Не удалось удалить местоположение",
|
||||
"failed_load_location": "Не удалось загрузить местоположение",
|
||||
"failed_update_location": "Не удалось обновить местоположение",
|
||||
"location_deleted": "Местоположение удалено",
|
||||
"location_updated": "Местоположение обновлено"
|
||||
},
|
||||
"update_location": "Обновить локацию"
|
||||
},
|
||||
"maintenance": {
|
||||
@@ -490,7 +294,6 @@
|
||||
"locations": "Локации",
|
||||
"maintenance": "Техническое обслуживание и ремонт",
|
||||
"profile": "Профиль",
|
||||
"scanner": "Сканер",
|
||||
"search": "Поиск",
|
||||
"tools": "Инструменты"
|
||||
},
|
||||
@@ -500,10 +303,7 @@
|
||||
"currency_format": "Формат валюты",
|
||||
"current_password": "Текущий пароль",
|
||||
"delete_account": "Удалить аккаунт",
|
||||
"delete_account_confirm": "Вы уверены, что хотите удалить свою учётную запись? Если вы последний участник в группе, все ваши данные будут удалены. Это действие нельзя отменить.",
|
||||
"delete_account_sub": "Удалить свой аккаунт и все связанные с ним данные. Это действие невозможно отменить.",
|
||||
"delete_notifier_confirm": "Вы уверены, что хотите удалить этот уведомитель?",
|
||||
"display_legacy_header": "{currentValue, select, true {Отключить устаревший заголовок} false {Включить устаревший заголовок} other {Не достигнуто}}",
|
||||
"enabled": "Активен",
|
||||
"example": "Пример",
|
||||
"gen_invite": "Сгенерировать ссылку-приглашение",
|
||||
@@ -513,97 +313,32 @@
|
||||
"language": "Язык",
|
||||
"new_password": "Новый пароль",
|
||||
"no_notifiers": "Нет настроенных уведомлений",
|
||||
"no_override": "Без переопределения",
|
||||
"notifier_modal": "{ type, select, true {Изменить} false {Создать} other {Другое}} Уведомитель",
|
||||
"notifiers": "Уведомители",
|
||||
"notifiers_sub": "Получать уведомления о предстоящем обслуживании",
|
||||
"override_locale": "Переопределить язык даты и валюты",
|
||||
"test": "Тест",
|
||||
"theme_settings": "Настройки темы",
|
||||
"theme_settings_sub": "Настройки темы хранятся в локальном хранилище браузера. Вы можете изменить тему в любое время. Если у вас\n не удается установить тему, попробуйте перезапустить браузер.",
|
||||
"toast": {
|
||||
"account_deleted": "Ваша учётная запись была удалена.",
|
||||
"failed_change_password": "Не удалось изменить пароль.",
|
||||
"failed_create_notifier": "Не удалось создать уведомитель.",
|
||||
"failed_delete_account": "Не удалось удалить вашу учётную запись.",
|
||||
"failed_delete_notifier": "Не удалось удалить уведомитель.",
|
||||
"failed_get_currencies": "Не удалось получить валюты",
|
||||
"failed_test_notifier": "Не удалось протестировать уведомитель.",
|
||||
"failed_update_group": "Не удалось обновить группу",
|
||||
"failed_update_notifier": "Не удалось обновить уведомитель.",
|
||||
"group_updated": "Группа обновлена",
|
||||
"notifier_test_success": "Тест уведомителя выполнен успешно.",
|
||||
"password_changed": "Пароль успешно изменён."
|
||||
},
|
||||
"update_group": "Обновить группу",
|
||||
"update_language": "Обновить язык",
|
||||
"url": "URL",
|
||||
"user_profile": "Профиль пользователя",
|
||||
"user_profile_sub": "Приглашайте пользователей и управляйте своим аккаунтом."
|
||||
},
|
||||
"reports": {
|
||||
"label_generator": {
|
||||
"asset_end": "Конец активов",
|
||||
"asset_start": "Начало активов",
|
||||
"base_url": "Базовый URL",
|
||||
"bordered_labels": "Этикетки с рамками",
|
||||
"generate_page": "Создать страницу",
|
||||
"input_placeholder": "Введите здесь",
|
||||
"instruction_1": "Генератор этикеток Homebox — это инструмент, помогающий печатать этикетки для вашего инвентаря Homebox. Эти этикетки предназначены\n для заблаговременной печати, чтобы вы могли напечатать много этикеток и иметь их готовыми к применению",
|
||||
"instruction_2": "Таким образом, эти этикетки работают путём печати QR-кода URL и информации об инвентарном номере на этикетке. Если вы отключили\n инвентарные номера в настройках Homebox, вы всё равно можете использовать этот инструмент, но инвентарные номера не будут ссылаться ни на какой элемент",
|
||||
"instruction_3": "Эта функция находится на раннем этапе разработки и может измениться в будущих выпусках. Если у вас есть отзывы, пожалуйста,\n предоставьте их в '<a href=\"https://github.com/sysadminsmedia/homebox/discussions/53\">обсуждении GitHub</a>'",
|
||||
"label_height": "Высота этикетки",
|
||||
"label_width": "Ширина этикетки",
|
||||
"measure_type": "Тип измерения",
|
||||
"page_bottom_padding": "Отступ снизу страницы",
|
||||
"page_height": "Высота страницы",
|
||||
"page_left_padding": "Отступ слева страницы",
|
||||
"page_right_padding": "Отступ справа страницы",
|
||||
"page_top_padding": "Отступ сверху страницы",
|
||||
"page_width": "Ширина страницы",
|
||||
"qr_code_example": "Пример QR-кода",
|
||||
"tip_1": "Настройки по умолчанию подобраны для\n '<a href=\"https://www.avery.com/templates/5260\">этикеток Avery 5260</a>'. Если вы используете другие этикетки,\n вам нужно будет скорректировать настройки в соответствии с их параметрами.",
|
||||
"tip_2": "Если вы настраиваете параметры своих этикеток - учтите, размеры указаны в дюймах. В моём случае, для этикеток типа 5260, я обнаружил,\n что размеры, используемые в их шаблоне, не соответствовали тому, что было необходимо для печати внутри блоков.\n '<b>Будьте готовы к тому, что придётся использовать метод проб и ошибок.</b>'",
|
||||
"tip_3": "При печати обязательно:\n '<ol><li>Установите поля на 0 или Нет</li><li>Установите масштабирование на 100%</li><li>Отключите двустороннюю печать</li><li>Распечатайте тестовую страницу перед печатью нескольких страниц</li></ol>'",
|
||||
"tips": "Советы",
|
||||
"title": "Генератор этикеток",
|
||||
"toast": {
|
||||
"page_too_small_card": "Размер страницы слишком мал для размера карточки"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
"barcode_detected_message": "обнаружен штрих-код продукта",
|
||||
"barcode_fetch_data": "Получить данные о продукте",
|
||||
"error": "Произошла ошибка при сканировании",
|
||||
"invalid_url": "Недействительный URL штрих-кода",
|
||||
"no_sources": "Видеоисточники недоступны",
|
||||
"permission_denied": "Доступ к камере запрещён, пожалуйста, разрешите доступ к камере в настройках браузера",
|
||||
"select_video_source": "Выберите видеоисточник",
|
||||
"title": "Сканер",
|
||||
"unsupported": "Media Stream API не поддерживается без HTTPS"
|
||||
},
|
||||
"tools": {
|
||||
"actions": "Действия с инвентарем",
|
||||
"actions_set": {
|
||||
"create_missing_thumbnails": "Создать отсутствующие миниатюры",
|
||||
"create_missing_thumbnails_button": "Создать миниатюры",
|
||||
"create_missing_thumbnails_confirm": "Вы уверены, что хотите создать отсутствующие миниатюры? Это может занять некоторое время и не может быть приостановлено.",
|
||||
"create_missing_thumbnails_sub": "Создаёт миниатюры для всех вложений, поддерживаемых текущей конфигурацией. Это полезно для вложений, которые были загружены до выпуска Homebox v0.20.0. Это не перезапишет существующие миниатюры, а только создаст новые для вложений, у которых нет миниатюры. Обратите внимание, что миниатюры создаются в фоновом режиме и их создание может занять некоторое время.",
|
||||
"ensure_ids": "Проверить ID активов",
|
||||
"ensure_ids_button": "Проверить ID активов",
|
||||
"ensure_ids_confirm": "Вы уверены, что хотите убедиться, что все активы имеют ID? Это может занять некоторое время и не может быть отменено.",
|
||||
"ensure_ids_sub": "Гарантирует, что все вещи в вашем инвентаре будут иметь корректное поле asset_id. Это производится при помощи поиска самого большого текущего значения поля asset_id в базе данных и применяет ко всем вещам новые значения, где они не были установлены в поле asset_id. Это производится в порядке сортировки по полю created_at.",
|
||||
"ensure_import_refs": "Проверка ссылок импорта",
|
||||
"ensure_import_refs_button": "Обеспечение импорта ссылок",
|
||||
"ensure_import_refs_sub": "Гарантирует что все вещи в Вашем инвентаре имеют корректное поле import_ref. Это производится при помощи генерации строки из 8 случайных символов для каждой вещи, где не поле import_ref не заполнено.",
|
||||
"set_primary_photo": "Установить основное фото",
|
||||
"set_primary_photo_button": "Установить основное фото",
|
||||
"set_primary_photo_confirm": "Вы уверены, что хотите установить основные фотографии? Это может занять некоторое время и не может быть отменено.",
|
||||
"set_primary_photo_sub": "В Homebox v0.10.0 мы добавили возможность отмечать вложенные фото как основное изображение. Это действие устанавливает первую фотографию во вложениях в качестве основной, если основное изображение еще не выбрано. '<a class=\"link\" href=\"https://github.com/hay-kot/homebox/pull/576\">'Посмотреть pull request #576'</a>'",
|
||||
"zero_datetimes": "Сбросить даты",
|
||||
"zero_datetimes_button": "Сбросить даты",
|
||||
"zero_datetimes_confirm": "Вы уверены, что хотите сбросить все значения даты и времени? Это может занять некоторое время и не может быть отменено.",
|
||||
"zero_datetimes_sub": "Сбрасывает значение полей даты и времени на начало даты в полном наборе. Это исправляет ошибку, когда сохранение значений времени на ранних этапах разработки сайта приводило к ошибке в точном отображении дат '<a class=\"link\" href=\"https://github.com/hay-kot/homebox/issues/236\" target=\"_blank\">'Посмотреть Issue #236 подробнее.'</a>'"
|
||||
},
|
||||
"actions_sub": "Применить действия ко всему вашему инвентарю. Это необратимое действие. '<b>'Будьте осторожны.'</b>'",
|
||||
@@ -614,7 +349,6 @@
|
||||
"export_sub": "Экспортирует файл в стандартном CSV формате для Homebox. Это экспортирует все Ваши вещи из Вашего инвентаря.",
|
||||
"import": "Импортировать инвентарь",
|
||||
"import_button": "Импортировать инвентарь",
|
||||
"import_ref_confirm": "Вы уверены, что хотите убедиться, что все активы имеют import_ref? Это может занять некоторое время и не может быть отменено.",
|
||||
"import_sub": "Импортировать стандартный CSV формат в Homebox. Без колонки '<code>'HB.import_ref'</code>' , это '<b>'не'</b>' перезапишет какую либо существующую вещь в вашем инвентаре, только добавит новые вещи. Строки с колонкой '<code>'HB.import_ref'</code>' будут объеденены с существующими вещами с теми же import_ref, если такие существуют."
|
||||
},
|
||||
"import_export_sub": "Импортировать или экспортировать ваш инвентарь в или из CSV файла. Это полезно при миграции вашего инвентаря в новый экземпляр Homebox.",
|
||||
@@ -627,14 +361,6 @@
|
||||
"bill_of_materials_button": "Сгенерировать список запчастей",
|
||||
"bill_of_materials_sub": "Генерирует CSV файл (значения, разделенные запятой), который может быть импортирован в приложении электронных таблиц. Это сводка вашего инвентаря с базовой информацией о вещах и их цене."
|
||||
},
|
||||
"reports_sub": "Создавайте различные отчеты для вашего инвентаря.",
|
||||
"toast": {
|
||||
"asset_success": "{results} активов было обновлено.",
|
||||
"failed_create_missing_thumbnails": "Не удалось создать отсутствующие миниатюры.",
|
||||
"failed_ensure_ids": "Не удалось обеспечить ID активов.",
|
||||
"failed_ensure_import_refs": "Не удалось обеспечить импорт ссылок.",
|
||||
"failed_set_primary_photos": "Не удалось установить основные фотографии.",
|
||||
"failed_zero_datetimes": "Не удалось сбросить значения даты и времени."
|
||||
}
|
||||
"reports_sub": "Создавайте различные отчеты для вашего инвентаря."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,8 +122,8 @@
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Nenašli sa žiadne výsledky",
|
||||
"placeholder": "Vybrať…",
|
||||
"search_placeholder": "Zadajte, čo chcete vyhľadať…"
|
||||
"placeholder": "Vybrať...",
|
||||
"search_placeholder": "Zadajte, čo chcete vyhľadať..."
|
||||
},
|
||||
"view": {
|
||||
"selectable": {
|
||||
@@ -176,7 +176,7 @@
|
||||
"select_location": "Vyberte miesto"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "Nie sú k dispozícii žiadne miesta. Pridajte nové miesta kliknutím na\n `<`span class=\"link-primary\"`>`Vytvoriť`<`/span`>` na navigačnej lište."
|
||||
"no_locations": "Nie sú k dispozícii žiadne miesta. Pridajte nové miesta cez\n `<`span class=\"link-primary\"`>`Vytvoriť`<`/span`>` na navigačnom paneli."
|
||||
}
|
||||
},
|
||||
"quick_menu": {
|
||||
@@ -211,7 +211,7 @@
|
||||
"items": "Položky",
|
||||
"join_discord": "Pripojte sa k Discordu",
|
||||
"labels": "Štítky",
|
||||
"loading": "Načítam…",
|
||||
"loading": "Načítam...",
|
||||
"locations": "Miesta",
|
||||
"maintenance": "Údržba",
|
||||
"name": "Názov",
|
||||
@@ -304,7 +304,7 @@
|
||||
"name": "Názov",
|
||||
"negate_labels": "Negovať vybrané označenia",
|
||||
"next_page": "Ďalšia strana",
|
||||
"no_attachments": "Neboli nájdené žiadne prílohy",
|
||||
"no_attachments": "Neboli nájdené žiadne prílohy.",
|
||||
"no_results": "Nenašli sa žiadne položky",
|
||||
"notes": "Poznámky",
|
||||
"only_with_photo": "Iba položky s fotografiou",
|
||||
@@ -537,7 +537,7 @@
|
||||
"input_placeholder": "Píšte sem",
|
||||
"instruction_1": "Generátor štítkov Homebox je nástroj, ktorý vám pomôže vytlačiť štítky pre inventár Homeboxu. Tie sú určené na\n to, aby ste mohli vytlačiť viacero štítkov naraz a mať ich pripravených vopred",
|
||||
"instruction_2": "Tieto štítky fungujú tak, že sa na štítok vytlačí QR kód URL adresy a informácia AssetID. Ak ste v nastaveniach Homeboxu\n AssetID zakázali, môžete tento nástroj i naďalej používať, ale AssetIT nebude odkazovať na žiadnu položku",
|
||||
"instruction_3": "Táto funkcia je v ranej fáze vývoja a v budúcich verziách sa môže zmeniť, ak máte spätnú väzbu, podeľte sa s ňou, prosím,\n v '<a href=\"https://github.com/sysadminsmedia/homebox/discussions/53\">'Diskusii na GitHube'</a>'",
|
||||
"instruction_3": "Táto funkcia je v ranej fáze vývoja a v budúcich verziách sa môže zmeniť, ak máte spätnú väzbu, podeľte sa s ňou, prosím,\n v <a href=\"https://github.com/sysadminsmedia/homebox/discussions/53\">'Diskusii na GitHube</a>",
|
||||
"label_height": "Výška štítka",
|
||||
"label_width": "Šírka štítku",
|
||||
"measure_type": "Typ merania",
|
||||
@@ -562,7 +562,7 @@
|
||||
"error": "Počas skenovania sa vyskytla chyba",
|
||||
"invalid_url": "Neplatná adresa URL čiarového kódu",
|
||||
"no_sources": "Nie sú k dispozícii žiadne zdroje videa",
|
||||
"permission_denied": "Prístup k kamere bol zamietnutý, povoľte prístup k kamere v nastaveniach prehliadača",
|
||||
"permission_denied": "Prístup k kamere bol zamietnutý, povoľte prístup k kamere v nastaveniach prehliadača.",
|
||||
"select_video_source": "Vyberte zdroj videa",
|
||||
"title": "Skener",
|
||||
"unsupported": "Media Stream API nie je podporované bez protokolu HTTPS"
|
||||
@@ -574,7 +574,7 @@
|
||||
"create_missing_thumbnails_button": "Vytvoriť náhľady",
|
||||
"create_missing_thumbnails_confirm": "Naozaj chcete vytvoriť chýbajúce náhľady? Môže to chvíľku trvať a akciu nie je možné pozastaviť.",
|
||||
"create_missing_thumbnails_sub": "Vytvorí miniatúry pre všetky prílohy, ktoré sú podporované aktuálnou konfiguráciou. Toto je užitočné pre prílohy, ktoré boli nahrané pred vydaním Homeboxu v0.20.0. Toto neprepíše existujúce miniatúry, vytvorí nové iba pre prílohy, ktoré nemajú miniatúru. Upozorňujeme, že miniatúry sa vytvárajú na pozadí a ich vytvorenie môže chvíľu trvať.",
|
||||
"ensure_ids": "Zabezpečenie identifikátorov položiek",
|
||||
"ensure_ids": "Zabezpečenie identifikátorov aktív",
|
||||
"ensure_ids_button": "Zabezpečenie identifikátorov aktív",
|
||||
"ensure_ids_confirm": "Naozaj chcete zabezpečiť, aby všetky prvky mali ID? Môže to chvíľu trvať a nedá sa to vrátiť späť.",
|
||||
"ensure_ids_sub": "Zabezpečí, aby všetky položky vo vašom inventári mali platné pole asset_id. To sa dosiahne tak, že sa v databáze zistí najvyššie aktuálne pole asset_id a na každú položku, ktorá nemá nastavené pole asset_id sa použije ďalšia hodnota. Toto sa vykonáva v poradí podľa poľa created_at.",
|
||||
@@ -604,10 +604,10 @@
|
||||
"import_export_sub": "Importujte a exportujte svoj inventár do a zo súboru CSV. To je užitočné pri migrácii inventára do novej inštancie Homeboxu.",
|
||||
"reports": "Správy",
|
||||
"reports_set": {
|
||||
"asset_labels": "Identifikačné štítky položiek",
|
||||
"asset_labels": "Štítky ID diela",
|
||||
"asset_labels_button": "Generátor štítkov",
|
||||
"asset_labels_sub": "Generuje tlačiteľné PDF štítkov pre rad Asset ID. Tieto nie sú špecifické pre váš inventár, takže štítky si môžete vytlačiť vopred a aplikovať ich na váš inventár, keď ich dostanete.",
|
||||
"bill_of_materials": "Súpis položiek",
|
||||
"bill_of_materials": "Faktúra za materiály",
|
||||
"bill_of_materials_button": "Generovať kusovník",
|
||||
"bill_of_materials_sub": "Vygeneruje súbor CSV (Comma Separated Values), ktorý možno importovať do tabuľkového procesora. Toto je súhrn vášho inventára so základnými informáciami o položkách a cenách."
|
||||
},
|
||||
|
||||
@@ -571,9 +571,6 @@
|
||||
"actions": "Dejanja inventarja",
|
||||
"actions_set": {
|
||||
"create_missing_thumbnails": "Ustvari manjkajoče predoglede",
|
||||
"create_missing_thumbnails_button": "Ustvari Predoglede",
|
||||
"create_missing_thumbnails_confirm": "Ali ste prepričani, da želite ustvariti manjkajoče predoglede? To lahko traja nekaj časa in ni možno ustaviti.",
|
||||
"create_missing_thumbnails_sub": "Ustvari predoglede za vse priloge, ki jih omogoča trenutna konfiguracija. To je uporabno za priloge, ki so bile naložene pred Homebox izdajo v0.20.0. To ne bo prepisalo obstoječih predogledov, ampak samo izdelalo nove za priloge ki še nimajo predogledov. Upoštevajte da so predogledi izdelani v ozadju in da to lahko traja nekaj časa.",
|
||||
"ensure_ids": "Zagotovi ID-je sredstev",
|
||||
"ensure_ids_button": "Zagotovi ID-je sredstev",
|
||||
"ensure_ids_confirm": "Ali ste prepričani, da želite zagotoviti, da imajo vsi predmeti ID? To lahko traja nekaj časa in ni možno razveljaviti.",
|
||||
@@ -614,7 +611,6 @@
|
||||
"reports_sub": "Ustvari različna poročila za svoj inventar.",
|
||||
"toast": {
|
||||
"asset_success": "{ results } predmetov je bilo posodobljenih.",
|
||||
"failed_create_missing_thumbnails": "Izdelava manjkajočih predogledov ni uspela.",
|
||||
"failed_ensure_ids": "Zagotavljanje IDjev predmetov ni uspelo.",
|
||||
"failed_ensure_import_refs": "Zagotavljanje import_ref ni uspelo.",
|
||||
"failed_set_primary_photos": "Nastavljanje primarnih fotografij ni uspelo.",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"title": "Importo skedarin CSV"
|
||||
},
|
||||
"outdated": {
|
||||
"current_version": "Version i Tanishëm",
|
||||
"current_version": "Version i Tanishëm:",
|
||||
"dismiss": "Largoje",
|
||||
"latest_version": "Versioni më i fundit",
|
||||
"new_version_available": "Një version i ri është i disponueshëm",
|
||||
@@ -96,7 +96,7 @@
|
||||
"parent_location": "Vendndodhja e prindit"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "Asnjë vendndodhje në dispozicion. Shto vendndodhje të reja përmes\n butonit '<span class=\"link-primary\">''Krijo'</span>' në shiritin e navigimit."
|
||||
"no_locations": "Asnjë vendndodhje në dispozicion. Shto vendndodhje të reja përmes\n butonit `< `span class=\"link-primary\" `> `Krijo `< `/span `> ` në shiritin e navigimit."
|
||||
}
|
||||
},
|
||||
"quick_menu": {
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
"select_location": "Välj en plats"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "Inga platser tillgängliga. Lägg till nya platser via\n '<span class=\"link-primary\">'Skapa'</span>'-knappen i navigationsmenyn."
|
||||
"no_locations": "Inga platser tillgängliga. Lägg till nya platser via\n `<`span class=\"link-primary\"`>`Skapa`<`/span`>`-knappen i navigationsmenyn."
|
||||
}
|
||||
},
|
||||
"quick_menu": {
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
{
|
||||
"components": {
|
||||
"app": {
|
||||
"create_modal": {
|
||||
"createAndAddAnother": "Başka bir tane oluşturmak ve eklemek için {shiftKey} + {enterKey} tuşlarını kullanın.",
|
||||
"enter": "Giriş",
|
||||
"shift": "Üst Karakter"
|
||||
},
|
||||
"import_dialog": {
|
||||
"change_warning": "Mevcut import_refs ile içe aktarmaların davranışı değişti. CSV dosyasında bir import_ref varsa, \nöğe CSV dosyasındaki değerlerle güncellenecektir.",
|
||||
"description": "Öğelerinizi, etiketlerinizi ve konumlarınızı içeren bir CSV dosyasını içe aktarın. Daha fazla\nbilgi için dökümanları okuyun.",
|
||||
"title": "CSV dosyasını içeri aktar",
|
||||
"toast": {
|
||||
"import_failed": "İçe aktarım başarısız. Lütfen daha sonra tekrar deneyin.",
|
||||
"import_success": "İçe aktarma başarılı!",
|
||||
"please_select_file": "Lütfen içe aktarılacak dosyayı seçin."
|
||||
}
|
||||
"title": "CSV dosyasını içeri aktar"
|
||||
},
|
||||
"outdated": {
|
||||
"current_version": "Güncel Sürüm",
|
||||
@@ -24,18 +14,6 @@
|
||||
"new_version_available_link": "Sürüm notlarını görüntülemek için buraya tıklayın"
|
||||
}
|
||||
},
|
||||
"color_selector": {
|
||||
"clear": "Rengi temizle",
|
||||
"color": "Renk",
|
||||
"no_color": "Renksiz",
|
||||
"no_color_selected": "Renk seçilmedi",
|
||||
"randomize": "Rastgele renk"
|
||||
},
|
||||
"form": {
|
||||
"password": {
|
||||
"toggle_show": "Şifreyi Göster/Gizle"
|
||||
}
|
||||
},
|
||||
"global": {
|
||||
"copy_text": {
|
||||
"documentation": "dokümantasyon",
|
||||
@@ -73,12 +51,7 @@
|
||||
"download": "Etiketi İndir",
|
||||
"print": "Etiketi yazdır",
|
||||
"server_print": "Sunucuda yazdır",
|
||||
"titles": "Etiketler",
|
||||
"toast": {
|
||||
"load_status_failed": "Durum yüklenemedi",
|
||||
"print_failed": "Etiket yazdırma başarısız",
|
||||
"print_success": "Etiket yazdırıldı"
|
||||
}
|
||||
"titles": "Etiketler"
|
||||
},
|
||||
"page_qr_code": {
|
||||
"page_url": "Sayfa URL'si",
|
||||
@@ -89,55 +62,20 @@
|
||||
}
|
||||
},
|
||||
"item": {
|
||||
"attachments_list": {
|
||||
"download": "İndir",
|
||||
"open_new_tab": "Yeni sekmede aç"
|
||||
},
|
||||
"create_modal": {
|
||||
"delete_photo": "Fotoğrafı sil",
|
||||
"item_description": "Ürün Açıklaması",
|
||||
"item_name": "Ürün Adı",
|
||||
"item_photo": "Öğe Fotoğrafı 📷",
|
||||
"item_quantity": "Öğe Miktarı",
|
||||
"parent_item": "Ana Öğe",
|
||||
"rotate_photo": "Fotoğrafı döndür",
|
||||
"set_as_primary_photo": "{isPrimary, select, true {non} false {} other {}}ana fotoğraf olarak ayarla",
|
||||
"title": "Eşya Oluştur",
|
||||
"toast": {
|
||||
"already_creating": "Zaten bir öğe oluşturuluyor",
|
||||
"create_failed": "Öğe oluşturulamadı",
|
||||
"create_success": "Öğe yaratıldı",
|
||||
"failed_load_parent": "Ana öğe yüklenemedi - lütfen elle seçin",
|
||||
"no_canvas_support": "Tarayıcınız canvas işlemlerini desteklemiyor",
|
||||
"please_select_location": "Lütfen bir konum seçin.",
|
||||
"rotate_failed": "Fotoğraf döndürme başarısız: { error }",
|
||||
"rotate_process_failed": "Döndürülmüş görüntü işlenemedi",
|
||||
"some_photos_failed": "{count, plural, =0 {Yüklenecek fotoğraf yok.} =1 {1 fotoğraf yüklenemedi.} other {Bazı fotoğraflar yüklenemedi.}}",
|
||||
"upload_failed": "Fotoğraf yüklenemedi: { photoName }",
|
||||
"upload_success": "{count, plural, =0 {Fotoğraf yüklenmedi.} =1 {Fotoğraf başarıyla yüklendi.} other {Tüm fotoğraflar başarıyla yüklendi.}}",
|
||||
"uploading_photos": "{count, plural, =0 {Yüklenecek fotoğraf yok} =1 {1 fotoğraf yükleniyor...} other {{count} fotoğraf yükleniyor...}}"
|
||||
},
|
||||
"upload_photos": "Fotoğrafları Yükle",
|
||||
"uploaded": "Yüklenen Fotoğraf"
|
||||
},
|
||||
"product_import": {
|
||||
"barcode": "Ürün barkodu",
|
||||
"db_source": "Veritabanı kaynağı",
|
||||
"error_invalid_barcode": "Geçersiz barkod girildi",
|
||||
"error_not_found": "Girilen barkoda ait ürün bulunamadı.",
|
||||
"search_item": "Ürün ara",
|
||||
"title": "Ürünü içe aktar"
|
||||
},
|
||||
"selector": {
|
||||
"no_results": "Sonuç Bulunamadı",
|
||||
"placeholder": "Seç…",
|
||||
"search_placeholder": "Aramak için yazın…"
|
||||
"upload_photos": "Fotoğrafları Yükle"
|
||||
},
|
||||
"view": {
|
||||
"selectable": {
|
||||
"card": "Kart",
|
||||
"items": "Öğeler",
|
||||
"no_items": "Görüntülenecek Öge Yok",
|
||||
"no_items": "Görüntülecek Öge Yok",
|
||||
"table": "Tablo"
|
||||
},
|
||||
"table": {
|
||||
@@ -150,31 +88,19 @@
|
||||
},
|
||||
"label": {
|
||||
"create_modal": {
|
||||
"label_color": "Etiket Rengi",
|
||||
"label_description": "Etiket Açıklaması",
|
||||
"label_name": "Etiket Adı",
|
||||
"title": "Etiket oluştur",
|
||||
"toast": {
|
||||
"already_creating": "Zaten bir etiket oluşturuluyor",
|
||||
"create_failed": "Etiket oluşturulamadı",
|
||||
"create_success": "Etiket oluşturuldu",
|
||||
"label_name_too_long": "Etiket adı 50 karakterden uzun olmamalıdır"
|
||||
}
|
||||
"title": "Etiket oluştur"
|
||||
},
|
||||
"selector": {
|
||||
"select_labels": "Etiketleri Seçin"
|
||||
"select_labels": "Etiketleri seçin"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"create_modal": {
|
||||
"location_description": "Konum Açıklaması",
|
||||
"location_name": "Konum Adı",
|
||||
"title": "Konum oluştur",
|
||||
"toast": {
|
||||
"already_creating": "Zaten bir konum oluşturuluyor",
|
||||
"create_failed": "Konum oluşturulamadı",
|
||||
"create_success": "Konum oluşturuldu"
|
||||
}
|
||||
"title": "Konum oluştur"
|
||||
},
|
||||
"selector": {
|
||||
"no_location_found": "Konum bulunamadı",
|
||||
@@ -183,7 +109,7 @@
|
||||
"select_location": "Konumu Seç"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "Mevcut konum yok. Navigasyon çubuğundaki\n.'<span class=\"link-primary\">'Oluştur'</span>' düğmesiyle yeni konumlar ekleyin."
|
||||
"no_locations": "Mevcut konum yok. Navigasyon çubuğundaki \n`<`span class=\"link-primary\"`>`Oluştur`<`/span`>` düğmesiyle yeni konumlar ekleyin."
|
||||
}
|
||||
},
|
||||
"quick_menu": {
|
||||
@@ -191,37 +117,31 @@
|
||||
"shortcut_hint": "Bir eylemi hızlı bir şekilde seçmek için sayı tuşlarını kullanın."
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"api_failure": "Backend API çağrısı başarısız oldu: "
|
||||
},
|
||||
"global": {
|
||||
"add": "Ekle",
|
||||
"archived": "Arşivlendi",
|
||||
"build": "Sürüm: { build }",
|
||||
"cancel": "İptal",
|
||||
"cancel": "İptal et",
|
||||
"confirm": "Onaylayın",
|
||||
"create": "Oluştur",
|
||||
"create_and_add": "Oluştur ve Bir Tane Daha Ekle",
|
||||
"create_subitem": "Alt Öğe Oluştur",
|
||||
"created": "Oluşturuldu",
|
||||
"delete": "Sil",
|
||||
"delete_confirm": "Bu öğeyi silmek istediğinizden emin misiniz? ",
|
||||
"demo_instance": "Bu bir tanıtım örneğidir",
|
||||
"details": "Detaylar",
|
||||
"duplicate": "Kopyala",
|
||||
"edit": "Düzenle",
|
||||
"email": "Elektronik posta",
|
||||
"follow_dev": "Geliştiriciyi takip edin",
|
||||
"footer": {
|
||||
"api_link": "'<a href=\"https://homebox.software/en/api/\" target=\"_blank\">'API'</a>'",
|
||||
"api_link": "<a href=\"https://homebox.software/en/api/\" target=\"_blank\">''API''</a>",
|
||||
"version_link": "'<'a href=\"https://github.com/sysadminsmedia/homebox/releases/tag/{ version }\" target=\"_blank\"'>' Sürüm: { version } Yapı: { build }'</a>"
|
||||
},
|
||||
"github": "GitHub Projesi",
|
||||
"github": "GitHub projesi",
|
||||
"insured": "Sigortalı",
|
||||
"items": "Öğeler",
|
||||
"join_discord": "Discord'a Katılın",
|
||||
"labels": "Etiketler",
|
||||
"loading": "Yükleniyor…",
|
||||
"locations": "Konumlar",
|
||||
"maintenance": "Bakım",
|
||||
"name": "İsim",
|
||||
@@ -229,14 +149,11 @@
|
||||
"password": "Şifre",
|
||||
"quantity": "Miktar",
|
||||
"read_docs": "Dokümanları okuyun",
|
||||
"return_home": "Anasayfaya Dön",
|
||||
"save": "Kaydet",
|
||||
"search": "Ara",
|
||||
"sign_out": "Oturumu kapat",
|
||||
"submit": "Gönder",
|
||||
"unknown": "Bilinmeyen",
|
||||
"update": "Güncelle",
|
||||
"updating": "Güncelleniyor",
|
||||
"value": "Değer",
|
||||
"version": "Versiyon:{ version }",
|
||||
"welcome": "Hoşgeldin, { username }"
|
||||
@@ -259,51 +176,29 @@
|
||||
"register": "Kaydolun",
|
||||
"remember_me": "Beni Hatırla",
|
||||
"set_email": "E-posta adresiniz nedir?",
|
||||
"set_name": "Adınız nedir?",
|
||||
"set_name": "Adın ne?",
|
||||
"set_password": "Şifrenizi belirleyin",
|
||||
"tagline": "Eşyalarınızı Takip Edin, Düzenleyin ve Yönetin.",
|
||||
"title": "Eşyalarınızı Düzenleyin ve Etiketleyin",
|
||||
"toast": {
|
||||
"invalid_email": "Geçersiz e-posta adresi",
|
||||
"invalid_email_password": "Geçersiz e-posta veya şifre",
|
||||
"login_success": "Başarıyla oturum açıldı",
|
||||
"problem_registering": "Kullanıcı kaydedilirken sorun oluştu",
|
||||
"user_registered": "Kullanıcı kaydedildi"
|
||||
}
|
||||
"tagline": "Eşyalarınızı Takip Edin, Düzenleyin ve Yönetin."
|
||||
},
|
||||
"items": {
|
||||
"add": "Ekle",
|
||||
"advanced": "İleri Seviye",
|
||||
"archived": "Arşivlendi",
|
||||
"asset_id": "Öğe Kimliği",
|
||||
"associated_with_multiple": "Bu varlık kimliği birden fazla öğe ile ilişkilidir",
|
||||
"attachment": "Ek",
|
||||
"attachments": "Ekler",
|
||||
"changes_persisted_immediately": "Eklerde yapılan değişiklikler hemen kaydedilecektir",
|
||||
"created_at": "Oluşturulma",
|
||||
"custom_fields": "Özel Alanlar",
|
||||
"delete_attachment_confirm": "Bu eki silmek istediğinizden emin misiniz?",
|
||||
"delete_item_confirm": "Bu öğeyi silmek istediğinizden emin misiniz?",
|
||||
"description": "Açıklama",
|
||||
"details": "Detaylar",
|
||||
"drag_and_drop": "Dosyaları buraya sürükleyip bırakın veya dosyaları seçmek için tıklayın",
|
||||
"edit": {
|
||||
"edit_attachment_dialog": {
|
||||
"attachment_title": "Ek Başlığı",
|
||||
"attachment_type": "Ek Türü",
|
||||
"primary_photo": "Ana Fotoğraf",
|
||||
"primary_photo_sub": "Bu seçenek yalnızca fotoğraflar için kullanılabilir. Yalnızca bir fotoğraf birincil olarak seçilebilir. Bu seçeneği seçerseniz, varsa mevcut ana fotoğraf seçili durumdan kaldırılır.",
|
||||
"select_type": "Tür seçin",
|
||||
"title": "Eki Düzenle"
|
||||
}
|
||||
},
|
||||
"edit_details": "Detayları Düzenle",
|
||||
"field_selector": "Alan Seçici",
|
||||
"field_selector": "alan seçici",
|
||||
"field_value": "Alan Değeri",
|
||||
"first": "Birinci",
|
||||
"include_archive": "Arşivlenen Öğeleri Dahil Et",
|
||||
"insured": "Sigortalı",
|
||||
"invalid_asset_id": "Geçersiz Varlık Kimliği",
|
||||
"last": "Son",
|
||||
"lifetime_warranty": "Ömür Boyu Garanti",
|
||||
"location": "Konum",
|
||||
@@ -314,11 +209,8 @@
|
||||
"name": "İsim",
|
||||
"negate_labels": "Seçili Etiketleri Yoksay",
|
||||
"next_page": "Sonraki Sayfa",
|
||||
"no_attachments": "Ek bulunamadı",
|
||||
"no_results": "Öğe Bulunamadı",
|
||||
"notes": "Notlar",
|
||||
"only_with_photo": "Sadece fotoğraflı öğeler",
|
||||
"only_without_photo": "Sadece fotoğrafsız öğeler",
|
||||
"options": "Seçenekler",
|
||||
"order_by": "Sıralama ölçütü",
|
||||
"pages": "Sayfa { page }/{ totalPages }",
|
||||
@@ -336,86 +228,42 @@
|
||||
"receipts": "Faturalar",
|
||||
"reset_search": "Aramayı Sıfırla",
|
||||
"results": "{ total } Sonuç",
|
||||
"select_field": "Alan seçin",
|
||||
"serial_number": "Seri Numarası",
|
||||
"show_advanced_view_options": "Gelişmiş Seçenekleri Göster",
|
||||
"sold_at": "Satıldığı Yer",
|
||||
"sold_details": "Satış Detayları",
|
||||
"sold_price": "Satış Fiyatı",
|
||||
"sold_to": "Satılan Kişi",
|
||||
"sync_child_locations": "Alt öğelerin konumlarını senkronize et",
|
||||
"tip_1": "Konum ve etiket filtreleri 'veya' işlemini kullanır. Eğer birden fazla seçilirse sadece biri \neşleştirme için kullanılacaktır.",
|
||||
"tip_2": "'#' ile başlayan aramalar bir varlık kimliğini sorgular (örneğin '#000-001')",
|
||||
"tip_3": "Alan filtreleri 'VEYA' işlemini kullanır. Birden fazla seçenek seçilirse, eşleşme için yalnızca birinin \nkarşılanması yeterlidir.",
|
||||
"tips": "İpuçları",
|
||||
"tips_sub": "Arama İpuçları",
|
||||
"toast": {
|
||||
"asset_not_found": "Varlık bulunamadı",
|
||||
"attachment_deleted": "Ek silindi",
|
||||
"attachment_updated": "Ek güncellendi",
|
||||
"attachment_uploaded": "Ek yüklendi",
|
||||
"child_items_location_no_longer_synced": "Alt öğelerin konumları artık bu öğeyle senkronize edilmeyecek.",
|
||||
"child_items_location_synced": "Alt öğelerin konumları bu öğeyle senkronize edildi",
|
||||
"child_location_desync": "Konumu değiştirmek, üst öğenin konumundan senkronizasyonu kaldıracaktır",
|
||||
"error_loading_parent_data": "Üst veriler yüklenmeye çalışılırken bir hata oluştu",
|
||||
"failed_adjust_quantity": "Miktar ayarlanamadı",
|
||||
"failed_delete_attachment": "Ek silinemedi",
|
||||
"failed_delete_item": "Öğe silinemedi",
|
||||
"failed_duplicate_item": "Öğe çoğaltılamadı",
|
||||
"failed_load_asset": "Varlık yüklenemedi",
|
||||
"failed_load_item": "Öğe yüklenemedi",
|
||||
"failed_load_items": "Öğeler yüklenemedi",
|
||||
"failed_save": "Öğe kaydedilemedi",
|
||||
"failed_save_no_location": "Öğe kaydedilemedi: konum seçilmedi",
|
||||
"failed_update_attachment": "Ek güncellenemedi",
|
||||
"failed_upload_attachment": "Ek yüklenemedi",
|
||||
"item_deleted": "Öğe silindi",
|
||||
"item_saved": "Öğe kaydedildi",
|
||||
"quantity_cannot_negative": "Miktar negatif olamaz"
|
||||
},
|
||||
"updated_at": "Güncelleme",
|
||||
"updated_at": "Güncellendiği Zaman",
|
||||
"warranty": "Garanti",
|
||||
"warranty_details": "Garanti Bilgileri",
|
||||
"warranty_expires": "Garanti Bitiş Tarihi"
|
||||
},
|
||||
"labels": {
|
||||
"label_delete_confirm": "Bu etiketi silmek istediğinizden emin misiniz? Bu eylem geri alınamaz.",
|
||||
"no_results": "Etiket Bulunamadı",
|
||||
"toast": {
|
||||
"failed_delete_label": "Etiket silinemedi",
|
||||
"failed_load_label": "Etiket yüklenemedi",
|
||||
"failed_update_label": "Etiket güncellenemedi",
|
||||
"label_deleted": "Etiket silindi",
|
||||
"label_updated": "Etiket güncellendi"
|
||||
},
|
||||
"update_label": "Etiket Güncelle"
|
||||
},
|
||||
"languages": {
|
||||
"ca": "Katalanca",
|
||||
"cs-CZ": "Çekçe",
|
||||
"de": "Almanca",
|
||||
"en": "İngilizce",
|
||||
"es": "İspanyolca",
|
||||
"fi-FI": "Fince",
|
||||
"fr": "Fransızca",
|
||||
"hu": "Macarca",
|
||||
"id-ID": "Endonezce",
|
||||
"it": "İtalyanca",
|
||||
"ja-JP": "Japonca",
|
||||
"ko-KR": "Korece",
|
||||
"lb-LU": "Lüksemburgca (Lüksemburg)",
|
||||
"lt-LT": "Litvanca (Litvanya)",
|
||||
"nb-NO": "Norveççe (Bokmål)",
|
||||
"nl": "Hollandaca",
|
||||
"pl": "Lehçe",
|
||||
"pt-BR": "Brezilya Portekizcesi",
|
||||
"pt-PT": "Portekizce (Portekiz)",
|
||||
"ru": "Rusça",
|
||||
"sl": "Slovence",
|
||||
"sq-AL": "Arnavutça",
|
||||
"sv": "İsveççe",
|
||||
"ta-IN": "Tamilce",
|
||||
"th-TH": "Tayca",
|
||||
"tr": "Türkçe",
|
||||
"uk-UA": "Ukraynaca",
|
||||
"zh-CN": "Basitleştirilmiş Çince",
|
||||
@@ -429,22 +277,13 @@
|
||||
"locations": {
|
||||
"child_locations": "Alt konumlar",
|
||||
"collapse_tree": "Ağacı Daralt",
|
||||
"expand_tree": "Ağacı Genişlet",
|
||||
"location_items_delete_confirm": "Bu konumu ve tüm öğelerini silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.",
|
||||
"no_results": "Konum bulunamadı",
|
||||
"toast": {
|
||||
"failed_delete_location": "Konum silinemedi",
|
||||
"failed_load_location": "Konum yüklenemedi",
|
||||
"failed_update_location": "Konum güncellenemedi",
|
||||
"location_deleted": "Konum silindi",
|
||||
"location_updated": "Konum güncellendi"
|
||||
},
|
||||
"update_location": "Konumu Güncelle"
|
||||
},
|
||||
"maintenance": {
|
||||
"filter": {
|
||||
"both": "İkisi de",
|
||||
"completed": "Tamamlanmış",
|
||||
"completed": "Tamamlandı",
|
||||
"scheduled": "Planlanmış"
|
||||
},
|
||||
"list": {
|
||||
@@ -484,7 +323,6 @@
|
||||
"locations": "Konumlar",
|
||||
"maintenance": "Bakım",
|
||||
"profile": "Profil",
|
||||
"scanner": "Tarayıcı",
|
||||
"search": "Ara",
|
||||
"tools": "Araçlar"
|
||||
},
|
||||
@@ -494,9 +332,7 @@
|
||||
"currency_format": "Para Birimi Biçimi",
|
||||
"current_password": "Mevcut Şifre",
|
||||
"delete_account": "Hesabı Sil",
|
||||
"delete_account_confirm": "Hesabınızı silmek istediğinizden emin misiniz? Grubunuzun son üyesiyseniz tüm verileriniz silinir. Bu işlem geri alınamaz.",
|
||||
"delete_account_sub": "Hesabınızı ve ona bağlı tüm verileri silin. Bu işlem geri alınamaz.",
|
||||
"display_legacy_header": "{ currentValue, select, true {Eski Başlığı Devre Dışı Bırak} false {Eski Başlığı Etkinleştir} other {Vurulmadı}}",
|
||||
"enabled": "Etkinleştirildi",
|
||||
"example": "Örnek",
|
||||
"gen_invite": "Davet Bağlantısı Oluştur",
|
||||
@@ -506,46 +342,20 @@
|
||||
"language": "Dil",
|
||||
"new_password": "Yeni Şifre",
|
||||
"no_notifiers": "Yapılandırılmış bildirimci yok",
|
||||
"no_override": "Geçersiz kılma yok",
|
||||
"notifier_modal": "{ type, select, true {Edit} false {Create} other {Other}} Bildirici",
|
||||
"notifiers": "Bildirimde Bulunanlar",
|
||||
"notifiers_sub": "Yaklaşan bakım hatırlatmaları için bildirimler alın",
|
||||
"override_locale": "Tarih ve Para Birimi Dilini Geçersiz Kıl",
|
||||
"test": "Test",
|
||||
"theme_settings": "Tema Ayarları",
|
||||
"theme_settings_sub": "Tema ayarları tarayıcınızın yerel depolama alanında saklanır. Temayı istediğiniz zaman değiştirebilirsiniz. \nTemanızı ayarlamakta sorun yaşıyorsanız, tarayıcınızı yenilemeyi deneyin.",
|
||||
"toast": {
|
||||
"account_deleted": "Hesabınız silindi.",
|
||||
"failed_change_password": "Şifre değiştirilemedi.",
|
||||
"password_changed": "Şifre başarıyla değiştirildi."
|
||||
},
|
||||
"update_group": "Grubu Güncelle",
|
||||
"update_language": "Dili Güncelle",
|
||||
"url": "URL",
|
||||
"user_profile": "Kullanıcı Profili",
|
||||
"user_profile_sub": "Kullanıcıları davet edin ve hesabınızı yönetin."
|
||||
},
|
||||
"reports": {
|
||||
"label_generator": {
|
||||
"generate_page": "Sayfayı Oluştur",
|
||||
"label_height": "Etiket Yüksekliği",
|
||||
"label_width": "Etiket Genişliği",
|
||||
"page_height": "Sayfa Yüksekliği",
|
||||
"tips": "İpuçları",
|
||||
"title": "Etiket Oluşturucu",
|
||||
"toast": {
|
||||
"page_too_small_card": "Sayfa boyutu kart boyutu için çok küçük"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
"error": "Tarama yapılırken bir hata oluştu",
|
||||
"invalid_url": "Geçersiz barkod URL'si",
|
||||
"no_sources": "Video kaynağı mevcut değil",
|
||||
"permission_denied": "Kamera izni reddedildi, lütfen tarayıcı ayarlarınızda kameraya erişime izin verin",
|
||||
"select_video_source": "Video kaynağı seçin",
|
||||
"title": "Tarayıcı",
|
||||
"unsupported": "Media Stream API, HTTPS olmadan desteklenmez"
|
||||
"permission_denied": "Kamera izni reddedildi, lütfen tarayıcı ayarlarınızda kameraya erişime izin verin"
|
||||
},
|
||||
"tools": {
|
||||
"actions": "Envanter İşlemleri",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user