mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-02-25 17:23:11 -05:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38e20ba321 | ||
|
|
717d7ca0fb | ||
|
|
4913b90408 | ||
|
|
c430c8da33 | ||
|
|
8221c36a89 | ||
|
|
a899f46464 | ||
|
|
50a5b39836 | ||
|
|
7e26fb068f | ||
|
|
808f11da0a | ||
|
|
b3573dc078 |
@@ -1,3 +1,5 @@
|
||||
*/node_modules
|
||||
*/dist
|
||||
*/data/db
|
||||
*/data/db
|
||||
*/mealie/test
|
||||
*/mealie/.temp
|
||||
2
.github/workflows/dockerbuild.prod.yml
vendored
2
.github/workflows/dockerbuild.prod.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Docker Build Dev
|
||||
name: Docker Build Production
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
56
.github/workflows/dockerbuild.release.yml
vendored
Normal file
56
.github/workflows/dockerbuild.release.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: Docker Build Production
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
#
|
||||
# Get Release Version
|
||||
#
|
||||
- uses: oprypin/find-latest-tag@v1
|
||||
with:
|
||||
repository: hay-kot/mealie # The repository to scan.
|
||||
releases-only: true # We know that all relevant tags have a GitHub release for them.
|
||||
id: mealie_version # The step ID to refer to later.
|
||||
#
|
||||
# Checkout
|
||||
#
|
||||
- name: checkout code
|
||||
uses: actions/checkout@v2
|
||||
#
|
||||
# Setup QEMU
|
||||
#
|
||||
- name: Set up QEMU
|
||||
id: qemu
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
image: tonistiigi/binfmt:latest
|
||||
platforms: all
|
||||
#
|
||||
# Setup Buildx
|
||||
#
|
||||
- name: install buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
install: true
|
||||
#
|
||||
# Login to Docker Hub
|
||||
#
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
#
|
||||
# Build
|
||||
#
|
||||
- name: build the image
|
||||
run: |
|
||||
docker build --push \
|
||||
--tag hkotel/mealie:${{ steps.mealie_version.outputs.tag }} \
|
||||
--platform linux/amd64,linux/arm/v7,linux/arm64 .
|
||||
2
.github/workflows/pytest.yml
vendored
2
.github/workflows/pytest.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Set up python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
#----------------------------------------------
|
||||
# ----- install & configure poetry -----
|
||||
#----------------------------------------------
|
||||
|
||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"python.formatting.provider": "black",
|
||||
"python.pythonPath": ".venv/bin/python3.8",
|
||||
"python.pythonPath": ".venv/bin/python3.9",
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.enabled": true,
|
||||
"python.autoComplete.extraPaths": ["mealie", "mealie/mealie"],
|
||||
@@ -17,5 +17,6 @@
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"cSpell.words": [
|
||||
"performant"
|
||||
]
|
||||
],
|
||||
"search.mode": "reuseEditor"
|
||||
}
|
||||
|
||||
45
.vscode/tasks.json
vendored
45
.vscode/tasks.json
vendored
@@ -13,15 +13,42 @@
|
||||
"group": "test"
|
||||
},
|
||||
{
|
||||
"label": "Production: Build and Start Docker Compose",
|
||||
"command": "./dev/scripts/docker-compose.sh",
|
||||
"type": "shell",
|
||||
"args": [],
|
||||
"problemMatcher": ["$tsc"],
|
||||
"presentation": {
|
||||
"reveal": "always"
|
||||
},
|
||||
"group": "test"
|
||||
"label": "Production: Build and Start Docker Compose",
|
||||
"command": "./dev/scripts/docker-compose.sh",
|
||||
"type": "shell",
|
||||
"args": [],
|
||||
"problemMatcher": ["$tsc"],
|
||||
"presentation": {
|
||||
"reveal": "always"
|
||||
},
|
||||
"group": "test"
|
||||
},
|
||||
{
|
||||
"label": "Dev: Start local Backend",
|
||||
"command": "../${config:python.pythonPath}",
|
||||
"args": ["app.py"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/mealie/"
|
||||
},
|
||||
"type": "shell",
|
||||
"problemMatcher": [],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"group": "groupA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Dev: Start local Frontend",
|
||||
"command": "npm run serve",
|
||||
"type": "shell",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/frontend/"
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"group": "groupA"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
21
Caddyfile
Normal file
21
Caddyfile
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
auto_https off
|
||||
}
|
||||
|
||||
:80 {
|
||||
@proxied path /api/* /docs /openapi.json
|
||||
|
||||
root * /app/dist
|
||||
encode gzip
|
||||
uri strip_suffix /
|
||||
|
||||
handle @proxied {
|
||||
reverse_proxy http://127.0.0.1:9000
|
||||
}
|
||||
|
||||
handle {
|
||||
try_files {path}.html {path} /
|
||||
file_server
|
||||
}
|
||||
|
||||
}
|
||||
15
Dockerfile
15
Dockerfile
@@ -5,9 +5,9 @@ RUN npm install
|
||||
COPY ./frontend/ .
|
||||
RUN npm run build
|
||||
|
||||
FROM python:3.8-alpine
|
||||
FROM python:3.9-alpine
|
||||
|
||||
RUN apk add --no-cache libxml2-dev libxslt-dev libxml2
|
||||
RUN apk add --no-cache libxml2-dev libxslt-dev libxml2 caddy
|
||||
ENV ENV prod
|
||||
EXPOSE 80
|
||||
WORKDIR /app
|
||||
@@ -17,7 +17,6 @@ COPY ./pyproject.toml /app/
|
||||
RUN apk add --update --no-cache --virtual .build-deps \
|
||||
curl \
|
||||
g++ \
|
||||
py-lxml \
|
||||
python3-dev \
|
||||
musl-dev \
|
||||
gcc \
|
||||
@@ -31,9 +30,11 @@ RUN apk add --update --no-cache --virtual .build-deps \
|
||||
|
||||
|
||||
COPY ./mealie /app
|
||||
COPY ./Caddyfile /app
|
||||
COPY ./app_data/templates /app/data/templates
|
||||
RUN rm -rf /app/tests /app/.temp
|
||||
COPY --from=build-stage /app/dist /app/dist
|
||||
RUN rm -rf /app/test /app/.temp
|
||||
|
||||
|
||||
VOLUME [ "/app_data/" ]
|
||||
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "80"]
|
||||
VOLUME [ "/app/data/" ]
|
||||
RUN chmod +x /app/run.sh
|
||||
CMD /app/run.sh
|
||||
@@ -1,33 +0,0 @@
|
||||
FROM node:lts-alpine as build-stage
|
||||
WORKDIR /app
|
||||
COPY ./frontend/package*.json ./
|
||||
RUN npm install
|
||||
COPY ./frontend/ .
|
||||
RUN npm run build
|
||||
|
||||
FROM mrnr91/uvicorn-gunicorn-fastapi:python3.8
|
||||
|
||||
|
||||
COPY ./requirements.txt /app/requirements.txt
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update -y && \
|
||||
apt-get install -y python-pip python-dev git curl --no-install-recommends
|
||||
|
||||
RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | POETRY_HOME=/opt/poetry python && \
|
||||
cd /usr/local/bin && \
|
||||
ln -s /opt/poetry/bin/poetry && \
|
||||
poetry config virtualenvs.create false
|
||||
|
||||
COPY ./pyproject.toml ./app/poetry.lock* /app/
|
||||
|
||||
COPY ./mealie /app
|
||||
RUN poetry install --no-root --no-dev
|
||||
COPY --from=build-stage /app/dist /app/dist
|
||||
RUN rm -rf /app/test /app/.temp
|
||||
|
||||
ENV ENV prod
|
||||
ENV APP_MODULE "app:app"
|
||||
|
||||
VOLUME [ "/app/data" ]
|
||||
@@ -63,6 +63,7 @@ Mealie also provides a secure API for interactions from 3rd party applications.
|
||||
#### Meal Planner
|
||||
- Random Meal plan generation based off categories
|
||||
- Expose notes in the API to allow external applications to access relevant information for meal plans
|
||||
- Shopping Lists
|
||||
#### Database Import / Export
|
||||
- Easily Import / Export your recipes from the UI
|
||||
- Export recipes in into custom files using Jinja2 templates
|
||||
@@ -82,7 +83,7 @@ Contributions are what make the open source community such an amazing place to b
|
||||
|
||||
If you are not a coder, you can still contribute financially. financial contributions help me prioritize working on this project over others and helps me know that there is a real demand for project development.
|
||||
|
||||
<a href="https://www.buymeacoffee.com/haykot" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-green.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
|
||||
<a href="https://www.buymeacoffee.com/haykot" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-green.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 107px !important;" ></a>
|
||||
|
||||
<!-- LICENSE -->
|
||||
## License
|
||||
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
docker buildx build .
|
||||
0
dev/scripts/scrape_recipe.py
Normal file → Executable file
0
dev/scripts/scrape_recipe.py
Normal file → Executable file
@@ -1,17 +0,0 @@
|
||||
$CWD = Get-Location
|
||||
|
||||
$pyFolder = Join-Path -Path $CWD -ChildPath "mealie"
|
||||
$pyVenv = Join-Path -Path $CWD -ChildPath "/venv/Scripts/python.exe"
|
||||
$pyScript = Join-Path -Path $CWD -ChildPath "/mealie/app.py"
|
||||
|
||||
$pythonCommand = "powershell.exe -NoExit -Command $pyVenv $pyScript"
|
||||
|
||||
$vuePath = Join-Path -Path $CWD -ChildPath "/frontend"
|
||||
$npmCommand = "powershell.exe -NoExit -Command npm run serve"
|
||||
|
||||
wt -d $pyFolder "powershell.exe" $pythonCommand `; split-pane -d $vuePath "powershell.exe" $npmCommand
|
||||
|
||||
Start-Process chrome "http://127.0.0.1:8000/docs"
|
||||
Start-Process chrome "http://127.0.0.1:8080
|
||||
"
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# Use root/example as user/password credentials
|
||||
# Frontend/Backend Served via the same Uvicorn Server
|
||||
version: "3.1"
|
||||
services:
|
||||
mealie:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: Dockerfile.arm
|
||||
container_name: mealie
|
||||
restart: always
|
||||
ports:
|
||||
- 9090:80
|
||||
environment:
|
||||
db_type: sql
|
||||
volumes:
|
||||
- ./mealie/data/:/app/data
|
||||
@@ -10,6 +10,3 @@ services:
|
||||
- 9090:80
|
||||
environment:
|
||||
db_type: sqlite
|
||||
|
||||
# volumes:
|
||||
# - ./mealie/data/:/app/data
|
||||
|
||||
@@ -1,5 +1,50 @@
|
||||
# Release Notes
|
||||
|
||||
## v0.3.0
|
||||
|
||||
### Bug Fixes
|
||||
- Fixed open search on `/` when in input. - Closes #174
|
||||
- Error when importing recipe: KeyError: '@type' - Closes #145
|
||||
- Fixed Import Issue - bhg.com - Closes #138
|
||||
- Scraper not working with recipe containing HowToSection - Closes #73
|
||||
|
||||
### Features and Improvements
|
||||
- Improved Nextcloud Imports
|
||||
- Improved Recipe Parser!
|
||||
- Open search with `/` hotkey!
|
||||
- Database and App version are now split
|
||||
- Unified and improved snackbar notifications
|
||||
- New Category/Tag endpoints to filter all recipes by Category or Tag
|
||||
- Category sidebar now has show/hide behavior on mobile
|
||||
- Settings menu on mobile is improved
|
||||
- **Meal Planner**
|
||||
- You can now restrict recipe categories used for random meal-plan creation in the settings menu
|
||||
- Recipe picker dialog will now display recipes when the search bar is empty
|
||||
- Minor UI improvements
|
||||
- **Shopping lists!** Shopping list can now be generated from a meal plan. Currently ingredients are split by recipes or there is a beta feature that attempts to sort them by similarity.
|
||||
- **Recipe Viewer**
|
||||
- Categories, Tags, and Notes will now be displayed below the steps on smaller screens
|
||||
- **Recipe Editor**
|
||||
- Text areas now auto grow to fit content
|
||||
- Description, Steps, and Notes support Markdown! This includes inline html in Markdown.
|
||||
- **Imports**
|
||||
- A revamped dialog has been created to provide more information on restoring backups. Exceptions on the backend are now sent to the frontend and are easily viewable to see what went wrong when you restored a backup. This functionality will be ported over to the migrations in a future release.
|
||||
|
||||
## v0.2.1 - Hot Fixes!
|
||||
|
||||
### Features and Improvements
|
||||
- Fixes upload image error when no photo was scrapped
|
||||
- Fixes no last_recipe.json not updating
|
||||
- Added markdown rendering for notes
|
||||
- New notifications
|
||||
- Minor UI improvements
|
||||
- Recipe editor refactor
|
||||
- Settings/Theme models refactor
|
||||
|
||||
### Development / Misc
|
||||
- Added async file response for images, downloading files.
|
||||
- Breakup recipe view component
|
||||
|
||||
## v0.2.0 - Now with Test!
|
||||
This is, what I think, is a big release! Tons of new features and some great quality of life improvements with some additional features. You may find that I made promises to include some fixes/features in v0.2.0. The short of is I greatly underestimated the work needed to refactor the database to a usable state and integrate categories in a way that is useful for users. This shouldn't be taken as a sign that I'm dropping those feature requests or ignoring them. I felt it was better to push a release in the current state rather than drag on development to try and fulfil all of the promises I made.
|
||||
|
||||
@@ -56,7 +101,6 @@ This is, what I think, is a big release! Tons of new features and some great qua
|
||||
!!! error "Breaking Changes"
|
||||
- API endpoints have been refactored to adhear to a more consistent standard. This is a WIP and more changes are likely to occur.
|
||||
- Officially Dropped MongoDB Support
|
||||
- Mounting volume moved to different internal location due to development issues. New volume should be mounted as `mealie/data:/app_data/`. Volume mounts need to be changed.
|
||||
- Database Breaks! We have not yet implemented a database migration service. As such, upgrades cannot be done by simply pulling the image. You must first export your recipes, update your deployment, and then import your recipes. This pattern is likely to be how upgrades take place prior to v1.0. After v1.0 migrations will be done automatically.
|
||||
|
||||
## v0.1.0 - Initial Beta
|
||||
|
||||
@@ -14,7 +14,16 @@ There are VSCode tasks created in the .vscode folder. You can use these to quick
|
||||
|
||||
|
||||
## Without Docker
|
||||
?? TODO
|
||||
Prerequisites
|
||||
|
||||
- Python 3.8+
|
||||
- Poetry
|
||||
- Nodejs
|
||||
- npm
|
||||
|
||||
change directories into the mealie directory and run poetry install. cd into the frontend directory and run npm install. After installing dependencies, you can use vscode tasks to run the front and backend server. Use the command pallette to access the tasks.
|
||||
|
||||
Alternatively you can run `npm run serve` in the frontend directory and `python app.py` in the mealie directory to get everything up and running for development.
|
||||
|
||||
## Trouble Shooting
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Installation
|
||||
To deploy docker on your local network it is highly recommended to use docker to deploy the image straight from dockerhub. Using the docker-compose below you should be able to get a stack up and running easily by changing a few default values and deploying. Currently MongoDB and SQLite are supported. MongoDB support will be dropped in v0.2.0 so it is recommended to go with SQLite for new deployments. Postrgres support is planned, however for most loads you may find SQLite performant enough for most use cases.
|
||||
To deploy docker on your local network it is highly recommended to use docker to deploy the image straight from dockerhub. Using the docker-compose below you should be able to get a stack up and running easily by changing a few default values and deploying. Currently only SQLite is supported. Postrgres support is planned, however for most loads you may find SQLite performant enough.
|
||||
|
||||
|
||||
[Get Docker](https://docs.docker.com/get-docker/)
|
||||
@@ -14,7 +14,7 @@ Deployment with the Docker CLI can be done with `docker run` and specify the dat
|
||||
docker run \
|
||||
-e db_type='sqlite' \
|
||||
-p 9000:80 \
|
||||
-v `pwd`:'/app_data/' \
|
||||
-v `pwd`:'/app/data/' \
|
||||
hkotel/mealie:latest
|
||||
|
||||
```
|
||||
@@ -35,7 +35,7 @@ services:
|
||||
db_type: sqlite
|
||||
TZ: America/Anchorage
|
||||
volumes:
|
||||
- ./mealie/data/:/app_data
|
||||
- ./mealie/data/:/app/data
|
||||
|
||||
```
|
||||
|
||||
@@ -46,7 +46,7 @@ services:
|
||||
| db_type | sqlite | The database type to be used. Current Options 'sqlite' |
|
||||
| mealie_port | 9000 | The port exposed by mealie. **do not change this if you're running in docker** If you'd like to use another port, map 9000 to another port of the host. |
|
||||
| api_docs | True | Turns on/off access to the API documentation locally. |
|
||||
| TZ | | You should set your time zone accordingly so the date/time features work correctly |
|
||||
| TZ | UTC | You should set your time zone accordingly so the date/time features work correctly |
|
||||
|
||||
|
||||
## Deployed as a Python Application
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# Meal Planner
|
||||
|
||||
## Working with Meal Plans
|
||||
In Mealie you can create a mealplan based off the calendar inputs on the meal planner page. There is no limit to how long or how short a meal plan is. You may also create duplicate meal plans for the same date range. After selecting your date range, click on the card for each day and search through recipes to find your choice. After selecting a recipe for all meals save the plan. You can also randomly generate meal plans.
|
||||
In Mealie you can create a meal plan based off the calendar inputs on the meal planner page. There is no limit to how long or how short a meal plan is. You may also create duplicate meal plans for the same date range. After selecting your date range, click on the card for each day and search through recipes to find your choice. After selecting a recipe for all meals save the plan. You can also randomly generate meal plans.
|
||||
|
||||
To edit the meal in a meal plan simply select the edit button on the card in the timeline. Similarly, to delete a mealplan click the delete button on the card in the timeline. Currently there is no support to change the date range in a meal plan.
|
||||
To edit the meal in a meal plan simply select the edit button on the card in the timeline. Similarly, to delete a meal plan click the delete button on the card in the timeline. Currently there is no support to change the date range in a meal plan.
|
||||
|
||||
!!! warning
|
||||
In coming a future release recipes for meals will be restricted to specific categories.
|
||||
|
||||
## Shopping Lists
|
||||
For any meal plan created you can view a breakdown of all the ingredients and use an experimental sort function to sort similarly ingredients. This is a very new feature and results of the auto sort may vary.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@ Color themes can be created and set from the UI in the settings page. You can se
|
||||
## Backups
|
||||
Site backups can easily be taken and download from the UI. To import, simply select the backup you'd like to restore and check which items you'd like to import.
|
||||
|
||||
## Meal Planner Webhooks
|
||||
## Meal Planner
|
||||
In the meal planner section you can select categories to be used as apart of the random recipe selector in the meal plan creator.
|
||||
|
||||
Meal planner webhooks are post requests sent from Mealie to an external endpoint. The body of the message is the Recipe JSON of the scheduled meal. If no meal is schedule, no request is sent. The webhook functionality can be enabled or disabled as well as scheduled. Note that you must "Save Webhooks" prior to any changes taking affect server side.
|
||||
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
VUE_APP_API_BASE_URL=http://10.10.10.12:9921
|
||||
VUE_APP_API_BASE_URL=http://localhost:9000
|
||||
525
frontend/package-lock.json
generated
525
frontend/package-lock.json
generated
@@ -4,6 +4,37 @@
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@adapttive/vue-markdown": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@adapttive/vue-markdown/-/vue-markdown-3.0.3.tgz",
|
||||
"integrity": "sha512-nopu1itoXJ5CwXe70dACSAq4n17nybBRZqXDu2ZyHC+vjVNjPiSp4kuvdBZHKMVHBEC7YakW4b9Mfkr2Wvybfw==",
|
||||
"requires": {
|
||||
"highlight.js": "^10.4.0",
|
||||
"markdown-it": "^12.0.3",
|
||||
"markdown-it-abbr": "^1.0.4",
|
||||
"markdown-it-deflist": "^2.1.0",
|
||||
"markdown-it-emoji": "^2.0.0",
|
||||
"markdown-it-external-preview": "^1.0.4",
|
||||
"markdown-it-footnote": "^3.0.2",
|
||||
"markdown-it-ins": "^3.0.0",
|
||||
"markdown-it-katex": "npm:@iktakahiro/markdown-it-katex@^4.0.1",
|
||||
"markdown-it-mark": "^3.0.0",
|
||||
"markdown-it-sub": "^1.0.0",
|
||||
"markdown-it-sup": "^1.0.0",
|
||||
"markdown-it-task-lists": "^2.1.1",
|
||||
"markdown-it-toc-and-anchor": "^4.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"markdown-it-katex": {
|
||||
"version": "npm:@iktakahiro/markdown-it-katex@4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@iktakahiro/markdown-it-katex/-/markdown-it-katex-4.0.1.tgz",
|
||||
"integrity": "sha512-kGFooO7fIOgY34PSG8ZNVsUlKhhNoqhzW2kq94TNGa8COzh73PO4KsEoPOsQVG1mEAe8tg7GqG0FoVao0aMHaw==",
|
||||
"requires": {
|
||||
"katex": "^0.12.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npm.taobao.org/@babel/code-frame/download/@babel/code-frame-7.10.4.tgz?cache=0&sync_timestamp=1593522948158&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fcode-frame%2Fdownload%2F%40babel%2Fcode-frame-7.10.4.tgz",
|
||||
@@ -1354,6 +1385,11 @@
|
||||
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
|
||||
"dev": true
|
||||
},
|
||||
"@smartweb/vue-flash-message": {
|
||||
"version": "0.6.10",
|
||||
"resolved": "https://registry.npmjs.org/@smartweb/vue-flash-message/-/vue-flash-message-0.6.10.tgz",
|
||||
"integrity": "sha512-ceDUUzXI6FDscev36kZQvc2BO+MayOt6uJ2HSh9zoOkfa0PVIhmaoB56InlTTsK7MmlSIvPJpRB+Habdx3MtNw=="
|
||||
},
|
||||
"@soda/friendly-errors-webpack-plugin": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.0.tgz",
|
||||
@@ -1445,6 +1481,14 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/concat-stream": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.0.tgz",
|
||||
"integrity": "sha1-OU2+C7X+5Gs42JZzXoto7yOQ0A0=",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/connect": {
|
||||
"version": "3.4.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz",
|
||||
@@ -1487,6 +1531,14 @@
|
||||
"@types/range-parser": "*"
|
||||
}
|
||||
},
|
||||
"@types/form-data": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz",
|
||||
"integrity": "sha1-yayFsqX9GENbjIXZ7LUObWyJP/g=",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/glob": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz",
|
||||
@@ -1544,8 +1596,7 @@
|
||||
"@types/node": {
|
||||
"version": "14.14.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz",
|
||||
"integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A=="
|
||||
},
|
||||
"@types/normalize-package-data": {
|
||||
"version": "2.4.0",
|
||||
@@ -1562,8 +1613,7 @@
|
||||
"@types/qs": {
|
||||
"version": "6.9.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz",
|
||||
"integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ=="
|
||||
},
|
||||
"@types/range-parser": {
|
||||
"version": "1.2.3",
|
||||
@@ -1992,51 +2042,6 @@
|
||||
"unique-filename": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ssri": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz",
|
||||
"integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"figgy-pudding": "^3.5.1",
|
||||
"minipass": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@@ -2053,16 +2058,6 @@
|
||||
"minipass": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"terser-webpack-plugin": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz",
|
||||
@@ -2079,18 +2074,6 @@
|
||||
"terser": "^4.6.12",
|
||||
"webpack-sources": "^1.4.3"
|
||||
}
|
||||
},
|
||||
"vue-loader-v16": {
|
||||
"version": "npm:vue-loader@16.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.1.2.tgz",
|
||||
"integrity": "sha512-8QTxh+Fd+HB6fiL52iEVLKqE9N1JSlMXLR92Ijm6g8PZrwIxckgpqjPDWRP5TWxdiPaHR+alUWsnu1ShQOwt+Q==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"chalk": "^4.1.0",
|
||||
"hash-sum": "^2.0.0",
|
||||
"loader-utils": "^2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2572,6 +2555,11 @@
|
||||
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
|
||||
"dev": true
|
||||
},
|
||||
"asap": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
||||
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
||||
@@ -2670,8 +2658,7 @@
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
|
||||
"dev": true
|
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
|
||||
},
|
||||
"atob": {
|
||||
"version": "2.1.2",
|
||||
@@ -2813,8 +2800,7 @@
|
||||
"base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
|
||||
},
|
||||
"batch": {
|
||||
"version": "0.6.1",
|
||||
@@ -2855,6 +2841,16 @@
|
||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
||||
"dev": true
|
||||
},
|
||||
"bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
@@ -2981,6 +2977,11 @@
|
||||
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
|
||||
"dev": true
|
||||
},
|
||||
"browser-or-node": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-1.3.0.tgz",
|
||||
"integrity": "sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg=="
|
||||
},
|
||||
"browserify-aes": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||
@@ -3100,8 +3101,7 @@
|
||||
"buffer-from": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
|
||||
},
|
||||
"buffer-indexof": {
|
||||
"version": "1.1.1",
|
||||
@@ -3270,8 +3270,7 @@
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
|
||||
"dev": true
|
||||
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
@@ -3554,6 +3553,17 @@
|
||||
"integrity": "sha1-ovSEN6LKqaIkNueUvwceyeYc7fY=",
|
||||
"dev": true
|
||||
},
|
||||
"clipboard": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz",
|
||||
"integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"good-listener": "^1.2.2",
|
||||
"select": "^1.1.2",
|
||||
"tiny-emitter": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"clipboardy": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
|
||||
@@ -3714,7 +3724,6 @@
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
@@ -3722,8 +3731,7 @@
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npm.taobao.org/commander/download/commander-2.20.3.tgz?cache=0&sync_timestamp=1605992628233&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcommander%2Fdownload%2Fcommander-2.20.3.tgz",
|
||||
"integrity": "sha1-/UhehMA+tIgcIHIrpIA16FMa6zM=",
|
||||
"dev": true
|
||||
"integrity": "sha1-/UhehMA+tIgcIHIrpIA16FMa6zM="
|
||||
},
|
||||
"commondir": {
|
||||
"version": "1.0.1",
|
||||
@@ -3794,7 +3802,6 @@
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
|
||||
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
@@ -4029,8 +4036,7 @@
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
|
||||
"dev": true
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||
},
|
||||
"cosmiconfig": {
|
||||
"version": "5.2.1",
|
||||
@@ -4631,8 +4637,13 @@
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
|
||||
"dev": true
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
|
||||
},
|
||||
"delegate": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
|
||||
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
|
||||
"optional": true
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.2",
|
||||
@@ -4941,8 +4952,7 @@
|
||||
"entities": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
|
||||
"integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w=="
|
||||
},
|
||||
"errno": {
|
||||
"version": "0.1.8",
|
||||
@@ -5575,10 +5585,17 @@
|
||||
"integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM="
|
||||
},
|
||||
"fast-levenshtein": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npm.taobao.org/fast-levenshtein/download/fast-levenshtein-2.0.6.tgz",
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||
"dev": true
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz",
|
||||
"integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==",
|
||||
"requires": {
|
||||
"fastest-levenshtein": "^1.0.7"
|
||||
}
|
||||
},
|
||||
"fastest-levenshtein": {
|
||||
"version": "1.0.12",
|
||||
"resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz",
|
||||
"integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow=="
|
||||
},
|
||||
"faye-websocket": {
|
||||
"version": "0.11.3",
|
||||
@@ -5623,6 +5640,13 @@
|
||||
"schema-utils": "^2.5.0"
|
||||
}
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"filesize": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
|
||||
@@ -5769,7 +5793,6 @@
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
|
||||
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.6",
|
||||
@@ -5892,6 +5915,11 @@
|
||||
"has-symbols": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"get-port": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz",
|
||||
"integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw="
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
|
||||
@@ -5967,6 +5995,15 @@
|
||||
"slash": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"good-listener": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
|
||||
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"delegate": "^3.1.2"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
|
||||
@@ -6119,8 +6156,7 @@
|
||||
"highlight.js": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.5.0.tgz",
|
||||
"integrity": "sha512-xTmvd9HiIHR6L53TMC7TKolEj65zG1XU+Onr8oi86mYa+nLcIbxTTWkpW7CsEwv/vK7u1zb8alZIMLDqqN6KTw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-xTmvd9HiIHR6L53TMC7TKolEj65zG1XU+Onr8oi86mYa+nLcIbxTTWkpW7CsEwv/vK7u1zb8alZIMLDqqN6KTw=="
|
||||
},
|
||||
"hmac-drbg": {
|
||||
"version": "1.0.1",
|
||||
@@ -6300,6 +6336,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"http-basic": {
|
||||
"version": "8.1.3",
|
||||
"resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz",
|
||||
"integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==",
|
||||
"requires": {
|
||||
"caseless": "^0.12.0",
|
||||
"concat-stream": "^1.6.2",
|
||||
"http-response-object": "^3.0.1",
|
||||
"parse-cache-control": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"http-deceiver": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
|
||||
@@ -6356,6 +6403,21 @@
|
||||
"micromatch": "^3.1.10"
|
||||
}
|
||||
},
|
||||
"http-response-object": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz",
|
||||
"integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==",
|
||||
"requires": {
|
||||
"@types/node": "^10.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "10.17.51",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.51.tgz",
|
||||
"integrity": "sha512-KANw+MkL626tq90l++hGelbl67irOJzGhUJk6a1Bt8QHOeh9tztJx+L0AqttraWKinmZn7Qi5lJZJzx45Gq0dg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
@@ -6400,8 +6462,7 @@
|
||||
"ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
|
||||
},
|
||||
"iferr": {
|
||||
"version": "0.1.5",
|
||||
@@ -6501,8 +6562,7 @@
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Finherits%2Fdownload%2Finherits-2.0.4.tgz",
|
||||
"integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=",
|
||||
"dev": true
|
||||
"integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w="
|
||||
},
|
||||
"inquirer": {
|
||||
"version": "7.3.3",
|
||||
@@ -6971,8 +7031,7 @@
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
|
||||
"dev": true
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"isexe": {
|
||||
"version": "2.0.0",
|
||||
@@ -7170,6 +7229,14 @@
|
||||
"verror": "1.10.0"
|
||||
}
|
||||
},
|
||||
"katex": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.12.0.tgz",
|
||||
"integrity": "sha512-y+8btoc/CK70XqcHqjxiGWBOeIL8upbS0peTPXTvgrh21n1RiWWcIpSWM+4uXq+IAgNh9YYQWdc7LVDPDAEEAg==",
|
||||
"requires": {
|
||||
"commander": "^2.19.0"
|
||||
}
|
||||
},
|
||||
"killable": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
|
||||
@@ -7217,6 +7284,14 @@
|
||||
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
|
||||
"dev": true
|
||||
},
|
||||
"linkify-it": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.2.tgz",
|
||||
"integrity": "sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==",
|
||||
"requires": {
|
||||
"uc.micro": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"loader-fs-cache": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz",
|
||||
@@ -7410,6 +7485,108 @@
|
||||
"object-visit": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"markdown-it": {
|
||||
"version": "12.0.4",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.0.4.tgz",
|
||||
"integrity": "sha512-34RwOXZT8kyuOJy25oJNJoulO8L0bTHYWXcdZBYZqFnjIy3NgjeoM3FmPXIOFQ26/lSHYMr8oc62B6adxXcb3Q==",
|
||||
"requires": {
|
||||
"argparse": "^2.0.1",
|
||||
"entities": "~2.1.0",
|
||||
"linkify-it": "^3.0.1",
|
||||
"mdurl": "^1.0.1",
|
||||
"uc.micro": "^1.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"markdown-it-abbr": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-abbr/-/markdown-it-abbr-1.0.4.tgz",
|
||||
"integrity": "sha1-1mtTZFIcuz3Yqlna37ovtoZcj9g="
|
||||
},
|
||||
"markdown-it-deflist": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-deflist/-/markdown-it-deflist-2.1.0.tgz",
|
||||
"integrity": "sha512-3OuqoRUlSxJiuQYu0cWTLHNhhq2xtoSFqsZK8plANg91+RJQU1ziQ6lA2LzmFAEes18uPBsHZpcX6We5l76Nzg=="
|
||||
},
|
||||
"markdown-it-emoji": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-2.0.0.tgz",
|
||||
"integrity": "sha512-39j7/9vP/CPCKbEI44oV8yoPJTpvfeReTn/COgRhSpNrjWF3PfP/JUxxB0hxV6ynOY8KH8Y8aX9NMDdo6z+6YQ=="
|
||||
},
|
||||
"markdown-it-external-preview": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-external-preview/-/markdown-it-external-preview-1.0.4.tgz",
|
||||
"integrity": "sha512-kuhuUeL1vmunHdzzUKTSec3Xl30vBbeDu/fgnlPvvQIRcOnWlCK+6pT2ov9R5igaJ0oXS6GMiyDlE7QiL7bd8w==",
|
||||
"requires": {
|
||||
"browser-or-node": "^1.3.0",
|
||||
"buffer": "^6.0.3",
|
||||
"prismjs": "^1.22.0",
|
||||
"sync-request": "^6.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"requires": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"markdown-it-footnote": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-footnote/-/markdown-it-footnote-3.0.2.tgz",
|
||||
"integrity": "sha512-JVW6fCmZWjvMdDQSbOT3nnOQtd9iAXmw7hTSh26+v42BnvXeVyGMDBm5b/EZocMed2MbCAHiTX632vY0FyGB8A=="
|
||||
},
|
||||
"markdown-it-ins": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-ins/-/markdown-it-ins-3.0.1.tgz",
|
||||
"integrity": "sha512-32SSfZqSzqyAmmQ4SHvhxbFqSzPDqsZgMHDwxqPzp+v+t8RsmqsBZRG+RfRQskJko9PfKC2/oxyOs4Yg/CfiRw=="
|
||||
},
|
||||
"markdown-it-mark": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-mark/-/markdown-it-mark-3.0.1.tgz",
|
||||
"integrity": "sha512-HyxjAu6BRsdt6Xcv6TKVQnkz/E70TdGXEFHRYBGLncRE9lBFwDNLVtFojKxjJWgJ+5XxUwLaHXy+2sGBbDn+4A=="
|
||||
},
|
||||
"markdown-it-sub": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-sub/-/markdown-it-sub-1.0.0.tgz",
|
||||
"integrity": "sha1-N1/WAm6ufdywEkl/ZBEZXqHjr+g="
|
||||
},
|
||||
"markdown-it-sup": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-sup/-/markdown-it-sup-1.0.0.tgz",
|
||||
"integrity": "sha1-y5yf+RpSVawI8/09YyhuFd8KH8M="
|
||||
},
|
||||
"markdown-it-task-lists": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz",
|
||||
"integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA=="
|
||||
},
|
||||
"markdown-it-toc-and-anchor": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-toc-and-anchor/-/markdown-it-toc-and-anchor-4.2.0.tgz",
|
||||
"integrity": "sha512-DusSbKtg8CwZ92ztN7bOojDpP4h0+w7BVOPuA3PHDIaabMsERYpwsazLYSP/UlKedoQjOz21mwlai36TQ04EpA==",
|
||||
"requires": {
|
||||
"clone": "^2.1.0",
|
||||
"uslug": "^1.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"clone": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||
"integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18="
|
||||
}
|
||||
}
|
||||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
@@ -7427,6 +7604,11 @@
|
||||
"integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==",
|
||||
"dev": true
|
||||
},
|
||||
"mdurl": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
|
||||
"integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
|
||||
},
|
||||
"media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@@ -7532,14 +7714,12 @@
|
||||
"mime-db": {
|
||||
"version": "1.45.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz",
|
||||
"integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.28",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz",
|
||||
"integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mime-db": "1.45.0"
|
||||
}
|
||||
@@ -7764,6 +7944,13 @@
|
||||
"thenify-all": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.14.2",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
|
||||
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"nanomatch": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
|
||||
@@ -8154,6 +8341,14 @@
|
||||
"prelude-ls": "~1.1.2",
|
||||
"type-check": "~0.3.2",
|
||||
"word-wrap": "~1.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"fast-levenshtein": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ora": {
|
||||
@@ -8306,6 +8501,11 @@
|
||||
"safe-buffer": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"parse-cache-control": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz",
|
||||
"integrity": "sha1-juqz5U+laSD+Fro493+iGqzC104="
|
||||
},
|
||||
"parse-json": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz",
|
||||
@@ -9165,6 +9365,14 @@
|
||||
"renderkid": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"prismjs": {
|
||||
"version": "1.23.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz",
|
||||
"integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==",
|
||||
"requires": {
|
||||
"clipboard": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
@@ -9174,8 +9382,7 @@
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"dev": true
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"progress": {
|
||||
"version": "2.0.3",
|
||||
@@ -9183,6 +9390,14 @@
|
||||
"integrity": "sha1-foz42PW48jnBvGi+tOt4Vn1XLvg=",
|
||||
"dev": true
|
||||
},
|
||||
"promise": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz",
|
||||
"integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==",
|
||||
"requires": {
|
||||
"asap": "~2.0.6"
|
||||
}
|
||||
},
|
||||
"promise-inflight": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
|
||||
@@ -9369,7 +9584,6 @@
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
@@ -9710,8 +9924,7 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"safe-regex": {
|
||||
"version": "1.1.0",
|
||||
@@ -9775,6 +9988,12 @@
|
||||
"ajv-keywords": "^3.5.2"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
|
||||
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
|
||||
"optional": true
|
||||
},
|
||||
"select-hose": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
||||
@@ -10528,7 +10747,6 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
@@ -10634,6 +10852,24 @@
|
||||
"util.promisify": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"sync-request": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz",
|
||||
"integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==",
|
||||
"requires": {
|
||||
"http-response-object": "^3.0.1",
|
||||
"sync-rpc": "^1.2.1",
|
||||
"then-request": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"sync-rpc": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz",
|
||||
"integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==",
|
||||
"requires": {
|
||||
"get-port": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"table": {
|
||||
"version": "5.4.6",
|
||||
"resolved": "https://registry.npm.taobao.org/table/download/table-5.4.6.tgz?cache=0&sync_timestamp=1605825218994&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftable%2Fdownload%2Ftable-5.4.6.tgz",
|
||||
@@ -10777,6 +11013,31 @@
|
||||
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
|
||||
"dev": true
|
||||
},
|
||||
"then-request": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz",
|
||||
"integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==",
|
||||
"requires": {
|
||||
"@types/concat-stream": "^1.6.0",
|
||||
"@types/form-data": "0.0.33",
|
||||
"@types/node": "^8.0.0",
|
||||
"@types/qs": "^6.2.31",
|
||||
"caseless": "~0.12.0",
|
||||
"concat-stream": "^1.6.0",
|
||||
"form-data": "^2.2.0",
|
||||
"http-basic": "^8.1.1",
|
||||
"http-response-object": "^3.0.1",
|
||||
"promise": "^8.0.0",
|
||||
"qs": "^6.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "8.10.66",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz",
|
||||
"integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"thenify": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||
@@ -10843,6 +11104,12 @@
|
||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
|
||||
"dev": true
|
||||
},
|
||||
"tiny-emitter": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
|
||||
"optional": true
|
||||
},
|
||||
"tmp": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npm.taobao.org/tmp/download/tmp-0.0.33.tgz",
|
||||
@@ -10995,8 +11262,12 @@
|
||||
"typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
|
||||
"dev": true
|
||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||
},
|
||||
"uc.micro": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.4.10",
|
||||
@@ -11098,6 +11369,11 @@
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"dev": true
|
||||
},
|
||||
"unorm": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz",
|
||||
"integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA=="
|
||||
},
|
||||
"unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
@@ -11221,6 +11497,14 @@
|
||||
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
|
||||
"dev": true
|
||||
},
|
||||
"uslug": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/uslug/-/uslug-1.0.4.tgz",
|
||||
"integrity": "sha1-uaIvCRTgqGFAYz2swwLl9PpFBnc=",
|
||||
"requires": {
|
||||
"unorm": ">= 1.0.0"
|
||||
}
|
||||
},
|
||||
"util": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
|
||||
@@ -11241,8 +11525,7 @@
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
},
|
||||
"util.promisify": {
|
||||
"version": "1.0.1",
|
||||
@@ -11835,7 +12118,11 @@
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
|
||||
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"bindings": "^1.5.0",
|
||||
"nan": "^2.12.1"
|
||||
}
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "3.1.0",
|
||||
@@ -12137,7 +12424,11 @@
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
|
||||
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"bindings": "^1.5.0",
|
||||
"nan": "^2.12.1"
|
||||
}
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "3.1.0",
|
||||
|
||||
@@ -9,8 +9,11 @@
|
||||
"i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@adapttive/vue-markdown": "^3.0.3",
|
||||
"@smartweb/vue-flash-message": "^0.6.10",
|
||||
"axios": "^0.21.1",
|
||||
"core-js": "^3.8.2",
|
||||
"fast-levenshtein": "^3.0.0",
|
||||
"fuse.js": "^6.4.6",
|
||||
"qs": "^6.9.6",
|
||||
"v-jsoneditor": "^1.4.2",
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<title> Mealie </title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" href="./styles/global.css">
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
*::-webkit-scrollbar {
|
||||
width: 0.25rem;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: lightgray;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background: grey;
|
||||
}
|
||||
@@ -1,16 +1,22 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<v-app-bar clipped-left dense app color="primary" dark class="d-print-none">
|
||||
<v-btn @click="$router.push('/')" icon>
|
||||
<v-icon size="40"> mdi-silverware-variant </v-icon>
|
||||
</v-btn>
|
||||
<router-link to="/">
|
||||
<v-btn icon>
|
||||
<v-icon size="40"> mdi-silverware-variant </v-icon>
|
||||
</v-btn>
|
||||
</router-link>
|
||||
|
||||
<div btn class="pl-2">
|
||||
<v-toolbar-title @click="$router.push('/')">Mealie</v-toolbar-title>
|
||||
<v-toolbar-title style="cursor: pointer" @click="$router.push('/')"
|
||||
>Mealie
|
||||
</v-toolbar-title>
|
||||
</div>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
<v-expand-x-transition>
|
||||
<SearchBar
|
||||
ref="mainSearchBar"
|
||||
class="mt-7"
|
||||
v-if="search"
|
||||
:show-results="true"
|
||||
@@ -29,6 +35,7 @@
|
||||
<SnackBar />
|
||||
<router-view></router-view>
|
||||
</v-container>
|
||||
<FlashMessage :position="'right bottom'"></FlashMessage>
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
@@ -54,6 +61,13 @@ export default {
|
||||
this.search = false;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
window.addEventListener("keyup", e => {
|
||||
if (e.key == "/" && !document.activeElement.id.startsWith('input') ) {
|
||||
this.search = !this.search;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$store.dispatch("initTheme");
|
||||
@@ -94,5 +108,53 @@ export default {
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.notify-info-color {
|
||||
border: 1px, solid, var(--v-info-base) !important;
|
||||
border-left: 3px, solid, var(--v-info-base) !important;
|
||||
background-color: var(--v-info-base) !important;
|
||||
}
|
||||
|
||||
.notify-warning-color {
|
||||
border: 1px, solid, var(--v-warning-base) !important;
|
||||
border-left: 3px, solid, var(--v-warning-base) !important;
|
||||
background-color: var(--v-warning-base) !important;
|
||||
}
|
||||
|
||||
.notify-error-color {
|
||||
border: 1px, solid, var(--v-error-base) !important;
|
||||
border-left: 3px, solid, var(--v-error-base) !important;
|
||||
background-color: var(--v-error-base) !important;
|
||||
}
|
||||
|
||||
.notify-success-color {
|
||||
border: 1px, solid, var(--v-success-base) !important;
|
||||
border-left: 3px, solid, var(--v-success-base) !important;
|
||||
background-color: var(--v-success-base) !important;
|
||||
}
|
||||
|
||||
.notify-base {
|
||||
color: white !important;
|
||||
margin-right: 60px;
|
||||
margin-bottom: -5px;
|
||||
opacity: 0.9 !important;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: 0.25rem;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: lightgray;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background: grey;
|
||||
}
|
||||
|
||||
.notify-base {
|
||||
color: white !important;
|
||||
margin-right: 60px;
|
||||
margin-bottom: -5px;
|
||||
opacity: 0.9 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,8 +6,9 @@ import themes from "./api/themes";
|
||||
import migration from "./api/migration";
|
||||
import myUtils from "./api/upload";
|
||||
import category from "./api/category";
|
||||
import meta from "./api/meta";
|
||||
|
||||
// import api from "../api";
|
||||
// import api from "@/api";
|
||||
|
||||
export default {
|
||||
recipes: recipe,
|
||||
@@ -18,4 +19,5 @@ export default {
|
||||
migrations: migration,
|
||||
utils: myUtils,
|
||||
categories: category,
|
||||
meta: meta,
|
||||
};
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
const baseURL = "/api/";
|
||||
import axios from "axios";
|
||||
import store from "../store/store";
|
||||
import utils from "@/utils";
|
||||
|
||||
// look for data.snackbar in response
|
||||
function processResponse(response) {
|
||||
try {
|
||||
store.commit("setSnackBar", {
|
||||
text: response.data.snackbar.text,
|
||||
type: response.data.snackbar.type,
|
||||
});
|
||||
utils.notify.show(response.data.snackbar.text, response.data.snackbar.type);
|
||||
} catch (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const apiReq = {
|
||||
post: async function (url, data) {
|
||||
let response = await axios.post(url, data).catch(function (error) {
|
||||
post: async function(url, data) {
|
||||
let response = await axios.post(url, data).catch(function(error) {
|
||||
if (error.response) {
|
||||
processResponse(error.response);
|
||||
return error.response;
|
||||
@@ -27,8 +24,19 @@ const apiReq = {
|
||||
return response;
|
||||
},
|
||||
|
||||
put: async function (url, data) {
|
||||
let response = await axios.put(url, data).catch(function (error) {
|
||||
put: async function(url, data) {
|
||||
let response = await axios.put(url, data).catch(function(error) {
|
||||
if (error.response) {
|
||||
processResponse(error.response);
|
||||
return response;
|
||||
} else return;
|
||||
});
|
||||
processResponse(response);
|
||||
return response;
|
||||
},
|
||||
|
||||
get: async function(url, data) {
|
||||
let response = await axios.get(url, data).catch(function(error) {
|
||||
if (error.response) {
|
||||
processResponse(error.response);
|
||||
return response;
|
||||
@@ -38,19 +46,8 @@ const apiReq = {
|
||||
return response;
|
||||
},
|
||||
|
||||
get: async function (url, data) {
|
||||
let response = await axios.get(url, data).catch(function (error) {
|
||||
if (error.response) {
|
||||
processResponse(error.response);
|
||||
return response;
|
||||
} else return;
|
||||
});
|
||||
// processResponse(response);
|
||||
return response;
|
||||
},
|
||||
|
||||
delete: async function (url, data) {
|
||||
let response = await axios.delete(url, data).catch(function (error) {
|
||||
delete: async function(url, data) {
|
||||
let response = await axios.delete(url, data).catch(function(error) {
|
||||
if (error.response) {
|
||||
processResponse(error.response);
|
||||
return response;
|
||||
|
||||
@@ -8,9 +8,9 @@ const backupURLs = {
|
||||
// Backup
|
||||
available: `${backupBase}available`,
|
||||
createBackup: `${backupBase}export/database`,
|
||||
importBackup: (fileName) => `${backupBase}${fileName}/import`,
|
||||
deleteBackup: (fileName) => `${backupBase}${fileName}/delete`,
|
||||
downloadBackup: (fileName) => `${backupBase}${fileName}/download`,
|
||||
importBackup: fileName => `${backupBase}${fileName}/import`,
|
||||
deleteBackup: fileName => `${backupBase}${fileName}/delete`,
|
||||
downloadBackup: fileName => `${backupBase}${fileName}/download`,
|
||||
};
|
||||
|
||||
export default {
|
||||
|
||||
@@ -8,9 +8,10 @@ const mealPlanURLs = {
|
||||
all: `${prefix}all`,
|
||||
create: `${prefix}create`,
|
||||
thisWeek: `${prefix}this-week`,
|
||||
update: (planID) => `${prefix}${planID}`,
|
||||
delete: (planID) => `${prefix}${planID}`,
|
||||
update: planID => `${prefix}${planID}`,
|
||||
delete: planID => `${prefix}${planID}`,
|
||||
today: `${prefix}today`,
|
||||
shopping: planID => `${prefix}${planID}/shopping-list`,
|
||||
};
|
||||
|
||||
export default {
|
||||
@@ -43,4 +44,9 @@ export default {
|
||||
let response = await apiReq.put(mealPlanURLs.update(id), body);
|
||||
return response;
|
||||
},
|
||||
|
||||
async shoppingList(id) {
|
||||
let response = await apiReq.get(mealPlanURLs.shopping(id));
|
||||
return response.data;
|
||||
},
|
||||
};
|
||||
|
||||
20
frontend/src/api/meta.js
Normal file
20
frontend/src/api/meta.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import { baseURL } from "./api-utils";
|
||||
import { apiReq } from "./api-utils";
|
||||
|
||||
const prefix = baseURL + "debug";
|
||||
|
||||
const debugURLs = {
|
||||
version: `${prefix}/version`,
|
||||
lastRecipe: `${prefix}/last-recipe-json`,
|
||||
};
|
||||
|
||||
export default {
|
||||
async get_version() {
|
||||
let response = await apiReq.get(debugURLs.version);
|
||||
return response.data;
|
||||
},
|
||||
async getLastJson() {
|
||||
let response = await apiReq.get(debugURLs.lastRecipe);
|
||||
return response.data;
|
||||
},
|
||||
};
|
||||
@@ -8,13 +8,14 @@ const prefix = baseURL + "recipes/";
|
||||
|
||||
const recipeURLs = {
|
||||
allRecipes: baseURL + "recipes",
|
||||
allRecipesByCategory: prefix + "category",
|
||||
create: prefix + "create",
|
||||
createByURL: prefix + "create-url",
|
||||
recipe: (slug) => prefix + slug,
|
||||
update: (slug) => prefix + slug,
|
||||
delete: (slug) => prefix + slug,
|
||||
recipeImage: (slug) => `${prefix}${slug}/image`,
|
||||
updateImage: (slug) => `${prefix}${slug}/image`,
|
||||
recipe: slug => prefix + slug,
|
||||
update: slug => prefix + slug,
|
||||
delete: slug => prefix + slug,
|
||||
recipeImage: slug => `${prefix}${slug}/image`,
|
||||
updateImage: slug => `${prefix}${slug}/image`,
|
||||
};
|
||||
|
||||
export default {
|
||||
@@ -27,6 +28,14 @@ export default {
|
||||
return response;
|
||||
},
|
||||
|
||||
async getAllByCategory(categories) {
|
||||
let response = await apiReq.post(
|
||||
recipeURLs.allRecipesByCategory,
|
||||
categories
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async create(recipeData) {
|
||||
let response = await apiReq.post(recipeURLs.create, recipeData);
|
||||
return response.data;
|
||||
@@ -67,7 +76,7 @@ export default {
|
||||
keys: recipeKeys,
|
||||
num: num,
|
||||
},
|
||||
paramsSerializer: (params) => {
|
||||
paramsSerializer: params => {
|
||||
return qs.stringify(params, { arrayFormat: "repeat" });
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { baseURL } from "./api-utils";
|
||||
import { apiReq } from "./api-utils";
|
||||
|
||||
const prefix = baseURL + "themes/";
|
||||
const prefix = baseURL + "themes";
|
||||
|
||||
const settingsURLs = {
|
||||
allThemes: `${baseURL}themes`,
|
||||
specificTheme: (themeName) => `${prefix}themes/${themeName}`,
|
||||
createTheme: `${prefix}themes/create`,
|
||||
updateTheme: (themeName) => `${prefix}themes/${themeName}`,
|
||||
deleteTheme: (themeName) => `${prefix}themes/${themeName}`,
|
||||
specificTheme: themeName => `${prefix}/${themeName}`,
|
||||
createTheme: `${prefix}/create`,
|
||||
updateTheme: themeName => `${prefix}/${themeName}`,
|
||||
deleteTheme: themeName => `${prefix}/${themeName}`,
|
||||
};
|
||||
|
||||
export default {
|
||||
@@ -33,6 +33,7 @@ export default {
|
||||
colors: colors,
|
||||
};
|
||||
let response = await apiReq.put(settingsURLs.updateTheme(themeName), body);
|
||||
console.log(response.data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { apiReq } from "./api-utils";
|
||||
|
||||
export default {
|
||||
// import api from "../api";
|
||||
// import api from "@/api";
|
||||
async uploadFile(url, fileObject) {
|
||||
let response = await apiReq.post(url, fileObject, {
|
||||
headers: {
|
||||
|
||||
38
frontend/src/components/Debug/LastRecipe.vue
Normal file
38
frontend/src/components/Debug/LastRecipe.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<v-card>
|
||||
<v-card-title>Last Scrapped JSON Data</v-card-title>
|
||||
<v-card-text>
|
||||
<VJsoneditor
|
||||
@error="logError()"
|
||||
v-model="lastRecipeJson"
|
||||
height="1500px"
|
||||
:options="jsonEditorOptions"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VJsoneditor from "v-jsoneditor";
|
||||
import api from "@/api";
|
||||
export default {
|
||||
components: { VJsoneditor },
|
||||
data() {
|
||||
return {
|
||||
lastRecipeJson: {},
|
||||
jsonEditorOptions: {
|
||||
mode: "code",
|
||||
search: false,
|
||||
mainMenuBar: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.lastRecipeJson = await api.meta.getLastJson();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
37
frontend/src/components/Debug/LogFile.vue
Normal file
37
frontend/src/components/Debug/LogFile.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<v-card>
|
||||
<v-card-title>Last Scrapped JSON Data</v-card-title>
|
||||
<v-card-text>
|
||||
<VJsoneditor
|
||||
@error="logError()"
|
||||
v-model="lastRecipeJson"
|
||||
height="1500px"
|
||||
:options="jsonEditorOptions"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VJsoneditor from "v-jsoneditor";
|
||||
export default {
|
||||
components: { VJsoneditor },
|
||||
data() {
|
||||
return {
|
||||
lastRecipeJson: "",
|
||||
jsonEditorOptions: {
|
||||
mode: "code",
|
||||
search: false,
|
||||
mainMenuBar: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.lastRecipeJson = "Hello \n 123 \n 567"
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -26,7 +26,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import utils from "../../utils";
|
||||
import utils from "@/utils";
|
||||
import SearchDialog from "../UI/SearchDialog";
|
||||
export default {
|
||||
components: {
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../../api";
|
||||
import utils from "../../utils";
|
||||
import api from "@/api";
|
||||
import utils from "@/utils";
|
||||
import MealPlanCard from "./MealPlanCard";
|
||||
export default {
|
||||
components: {
|
||||
|
||||
@@ -85,8 +85,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../../api";
|
||||
import utils from "../../utils";
|
||||
import api from "@/api";
|
||||
import utils from "@/utils";
|
||||
import MealPlanCard from "./MealPlanCard";
|
||||
export default {
|
||||
components: {
|
||||
@@ -96,6 +96,7 @@ export default {
|
||||
return {
|
||||
isLoading: false,
|
||||
meals: [],
|
||||
items: [],
|
||||
|
||||
// Dates
|
||||
startDate: null,
|
||||
@@ -117,11 +118,12 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
let settings = await api.settings.requestAll();
|
||||
this.items = await api.recipes.getAllByCategory(settings.planCategories);
|
||||
},
|
||||
|
||||
computed: {
|
||||
items() {
|
||||
return this.$store.getters.getRecentRecipes;
|
||||
},
|
||||
actualStartDate() {
|
||||
return Date.parse(this.startDate);
|
||||
},
|
||||
|
||||
110
frontend/src/components/MealPlan/ShoppingListDialog.vue
Normal file
110
frontend/src/components/MealPlan/ShoppingListDialog.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="text-center">
|
||||
<v-dialog v-model="dialog" width="650">
|
||||
<v-card>
|
||||
<v-card-title class="headline">
|
||||
Shopping List
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn text color="accent" @click="group = !group">
|
||||
Group (Beta)
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-card-text v-if="group == false">
|
||||
<v-list
|
||||
dense
|
||||
v-for="(recipe, index) in ingredients"
|
||||
:key="`${index}-recipe`"
|
||||
>
|
||||
<v-subheader>{{ recipe.name }} </v-subheader>
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-list-item-group color="primary">
|
||||
<v-list-item
|
||||
v-for="(item, i) in recipe.recipeIngredient"
|
||||
:key="i"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title v-text="item"></v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list-item-group>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text v-else>
|
||||
<v-list dense>
|
||||
<v-list-item-group color="primary">
|
||||
<v-list-item v-for="(item, i) in rawIngredients" :key="i">
|
||||
<v-list-item-content>
|
||||
<v-list-item-title v-text="item"></v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list-item-group>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
|
||||
<v-divider></v-divider>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "@/api";
|
||||
const levenshtein = require("fast-levenshtein");
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dialog: false,
|
||||
planID: 0,
|
||||
ingredients: [],
|
||||
rawIngredients: [],
|
||||
group: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
openDialog: function(id) {
|
||||
this.dialog = true;
|
||||
this.planID = id;
|
||||
this.getIngredients();
|
||||
},
|
||||
async getIngredients() {
|
||||
this.ingredients = await api.mealPlans.shoppingList(this.planID);
|
||||
this.getRawIngredients();
|
||||
},
|
||||
getRawIngredients() {
|
||||
this.ingredients.forEach(element => {
|
||||
this.rawIngredients.push(element.recipeIngredient);
|
||||
});
|
||||
|
||||
this.rawIngredients = this.rawIngredients.flat();
|
||||
this.rawIngredients = this.levenshteinFilter(this.rawIngredients);
|
||||
},
|
||||
levenshteinFilter(source, maximum = 5) {
|
||||
let _source, matches, x, y;
|
||||
_source = source.slice();
|
||||
matches = [];
|
||||
for (x = _source.length - 1; x >= 0; x--) {
|
||||
let output = _source.splice(x, 1);
|
||||
for (y = _source.length - 1; y >= 0; y--) {
|
||||
if (levenshtein.get(output[0], _source[y]) <= maximum) {
|
||||
output.push(_source[y]);
|
||||
_source.splice(y, 1);
|
||||
x--;
|
||||
}
|
||||
}
|
||||
matches.push(output);
|
||||
}
|
||||
return matches.flat();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -12,28 +12,8 @@
|
||||
></v-file-input>
|
||||
</v-col>
|
||||
<v-col cols="3"></v-col>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
label="Total Time"
|
||||
v-model="value.totalTime"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col
|
||||
><v-text-field
|
||||
label="Prep Time"
|
||||
v-model="value.prepTime"
|
||||
></v-text-field
|
||||
></v-col>
|
||||
<v-col
|
||||
><v-text-field
|
||||
label="Cook Time / Perform Time"
|
||||
v-model="value.performTime"
|
||||
></v-text-field
|
||||
></v-col>
|
||||
</v-row>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-row dense>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('recipe.total-time')"
|
||||
@@ -61,21 +41,23 @@
|
||||
>
|
||||
</v-text-field>
|
||||
<v-textarea
|
||||
height="100"
|
||||
auto-grow
|
||||
min-height="100"
|
||||
:label="$t('recipe.description')"
|
||||
v-model="value.description"
|
||||
>
|
||||
</v-textarea>
|
||||
<div class="my-2"></div>
|
||||
<v-row dense disabled>
|
||||
<v-col sm="5">
|
||||
<v-col sm="4">
|
||||
<v-text-field
|
||||
:label="$t('recipe.servings')"
|
||||
v-model="value.recipeYield"
|
||||
class="rounded-sm"
|
||||
>
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col></v-col>
|
||||
<v-spacer></v-spacer>
|
||||
<v-rating
|
||||
class="mr-2 align-end"
|
||||
color="secondary darken-1"
|
||||
@@ -206,6 +188,7 @@
|
||||
</v-row>
|
||||
|
||||
<v-textarea
|
||||
auto-grow
|
||||
:label="$t('recipe.note')"
|
||||
v-model="value.notes[index]['text']"
|
||||
>
|
||||
@@ -238,17 +221,18 @@
|
||||
elevation="0"
|
||||
@click="removeStep(index)"
|
||||
>
|
||||
<v-icon color="error">mdi-delete</v-icon> </v-btn
|
||||
>{{
|
||||
$t("recipe.step-index", { step: index + 1 })
|
||||
}}</v-card-title
|
||||
>
|
||||
<v-icon color="error">mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
{{ $t("recipe.step-index", { step: index + 1 }) }}
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-textarea
|
||||
auto-grow
|
||||
dense
|
||||
v-model="value.recipeInstructions[index]['text']"
|
||||
:key="generateKey('instructions', index)"
|
||||
></v-textarea>
|
||||
>
|
||||
</v-textarea>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-hover>
|
||||
@@ -270,8 +254,8 @@
|
||||
|
||||
<script>
|
||||
import draggable from "vuedraggable";
|
||||
import api from "../../../api";
|
||||
import utils from "../../../utils";
|
||||
import api from "@/api";
|
||||
import utils from "@/utils";
|
||||
import BulkAdd from "./BulkAdd";
|
||||
import ExtrasEditor from "./ExtrasEditor";
|
||||
export default {
|
||||
@@ -288,8 +272,8 @@ export default {
|
||||
drag: false,
|
||||
fileObject: null,
|
||||
rules: {
|
||||
required: (v) => !!v || this.$i18n.t("recipe.key-name-required"),
|
||||
whiteSpace: (v) =>
|
||||
required: v => !!v || this.$i18n.t("recipe.key-name-required"),
|
||||
whiteSpace: v =>
|
||||
!v ||
|
||||
v.split(" ").length <= 1 ||
|
||||
this.$i18n.t("recipe.no-white-space-allowed"),
|
||||
@@ -306,7 +290,7 @@ export default {
|
||||
methods: {
|
||||
async getCategories() {
|
||||
let response = await api.categories.get_all();
|
||||
this.categories = response.map((cat) => cat.name);
|
||||
this.categories = response.map(cat => cat.name);
|
||||
},
|
||||
uploadImage() {
|
||||
this.$emit("upload", this.fileObject);
|
||||
@@ -353,7 +337,7 @@ export default {
|
||||
|
||||
appendSteps(steps) {
|
||||
let processSteps = [];
|
||||
steps.forEach((element) => {
|
||||
steps.forEach(element => {
|
||||
processSteps.push({ text: element });
|
||||
});
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import utils from "../../utils";
|
||||
import utils from "@/utils";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
||||
@@ -1,50 +1,58 @@
|
||||
<template>
|
||||
<v-card
|
||||
color="accent"
|
||||
class="custom-transparent"
|
||||
class="custom-transparent d-flex justify-start align-center text-center "
|
||||
tile
|
||||
:width="`${timeCardWidth}`"
|
||||
v-if="totalTime || prepTime || performTime"
|
||||
>
|
||||
<v-card-text
|
||||
class="text-caption white--text"
|
||||
v-if="totalTime || prepTime || performTime"
|
||||
<v-card flat color="rgb(255, 0, 0, 0.0)">
|
||||
<v-icon large color="white" class="mx-2"> mdi-clock-outline </v-icon>
|
||||
</v-card>
|
||||
|
||||
<v-divider vertical color="white" class="py-1" v-if="totalTime">
|
||||
</v-divider>
|
||||
<v-card flat color="rgb(255, 0, 0, 0.0)" class=" my-2 " v-if="totalTime">
|
||||
<v-card-text class="white--text">
|
||||
<div>
|
||||
<strong> {{ $t("recipe.total-time") }} </strong>
|
||||
</div>
|
||||
<div>{{ totalTime }}</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-divider vertical color="white" class="py-1" v-if="prepTime"> </v-divider>
|
||||
|
||||
<v-card
|
||||
flat
|
||||
color="rgb(255, 0, 0, 0.0)"
|
||||
class="white--text my-2 "
|
||||
v-if="prepTime"
|
||||
>
|
||||
<v-row align="center" dense>
|
||||
<v-col :cols="iconColumn">
|
||||
<v-icon large color="white"> mdi-clock-outline </v-icon>
|
||||
</v-col>
|
||||
<v-divider
|
||||
vertical
|
||||
color="white"
|
||||
class="my-1"
|
||||
v-if="totalTime"
|
||||
></v-divider>
|
||||
<v-col v-if="totalTime">
|
||||
<div><strong> {{ $t("recipe.total-time") }} </strong></div>
|
||||
<div>{{ totalTime }}</div>
|
||||
</v-col>
|
||||
<v-divider
|
||||
vertical
|
||||
color="white"
|
||||
class="my-1"
|
||||
v-if="prepTime"
|
||||
></v-divider>
|
||||
<v-col v-if="prepTime">
|
||||
<div><strong> {{ $t("recipe.prep-time") }} </strong></div>
|
||||
<div>{{ prepTime }}</div>
|
||||
</v-col>
|
||||
<v-divider
|
||||
vertical
|
||||
color="white"
|
||||
class="my-1"
|
||||
v-if="performTime"
|
||||
></v-divider>
|
||||
<v-col v-if="performTime">
|
||||
<div><strong> {{ $t("recipe.perform-time") }} </strong></div>
|
||||
<div>{{ performTime }}</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-text class="white--text">
|
||||
<div>
|
||||
<strong> {{ $t("recipe.prep-time") }} </strong>
|
||||
</div>
|
||||
<div>{{ prepTime }}</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-divider vertical color="white" class="my-1" v-if="performTime">
|
||||
</v-divider>
|
||||
|
||||
<v-card
|
||||
flat
|
||||
color="rgb(255, 0, 0, 0.0)"
|
||||
class="white--text py-2 "
|
||||
v-if="performTime"
|
||||
>
|
||||
<v-card-text class="white--text">
|
||||
<div>
|
||||
<strong> {{ $t("recipe.perform-time") }} </strong>
|
||||
</div>
|
||||
<div>{{ performTime }}</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
@@ -59,7 +67,7 @@ export default {
|
||||
timeLength() {
|
||||
let times = [];
|
||||
let timeArray = [this.totalTime, this.prepTime, this.performTime];
|
||||
timeArray.forEach((element) => {
|
||||
timeArray.forEach(element => {
|
||||
if (element) {
|
||||
times.push(element);
|
||||
}
|
||||
@@ -83,10 +91,10 @@ export default {
|
||||
},
|
||||
timeCardWidth() {
|
||||
let timeArray = [this.totalTime, this.prepTime, this.performTime];
|
||||
let width = 120;
|
||||
timeArray.forEach((element) => {
|
||||
let width = 80;
|
||||
timeArray.forEach(element => {
|
||||
if (element) {
|
||||
width += 70;
|
||||
width += 95;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-card-title class="headline">
|
||||
{{ name }}
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
{{ description }}
|
||||
<div class="my-2"></div>
|
||||
<v-row dense disabled>
|
||||
<v-col>
|
||||
<v-btn
|
||||
v-if="yields"
|
||||
dense
|
||||
small
|
||||
:hover="false"
|
||||
type="label"
|
||||
:ripple="false"
|
||||
elevation="0"
|
||||
color="secondary darken-1"
|
||||
class="rounded-sm static"
|
||||
>
|
||||
{{ yields }}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-rating
|
||||
class="mr-2 align-end static"
|
||||
color="secondary darken-1"
|
||||
background-color="secondary lighten-3"
|
||||
length="5"
|
||||
:value="rating"
|
||||
></v-rating>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="4" lg="4">
|
||||
<h2 class="mb-4">{{$t('recipe.ingredients')}}</h2>
|
||||
<div
|
||||
v-for="(ingredient, index) in ingredients"
|
||||
:key="generateKey('ingredient', index)"
|
||||
>
|
||||
<v-checkbox
|
||||
hide-details
|
||||
class="ingredients"
|
||||
:label="ingredient"
|
||||
color="secondary"
|
||||
>
|
||||
</v-checkbox>
|
||||
</div>
|
||||
|
||||
<div v-if="categories[0]">
|
||||
<h2 class="mt-4">{{$t('recipe.categories')}}</h2>
|
||||
<v-chip
|
||||
class="ma-1"
|
||||
color="accent"
|
||||
dark
|
||||
v-for="category in categories"
|
||||
:key="category"
|
||||
>
|
||||
{{ category }}
|
||||
</v-chip>
|
||||
</div>
|
||||
|
||||
<div v-if="tags[0]">
|
||||
<h2 class="mt-4">{{$t('recipe.tags')}}</h2>
|
||||
<v-chip
|
||||
class="ma-1"
|
||||
color="accent"
|
||||
dark
|
||||
v-for="tag in tags"
|
||||
:key="tag"
|
||||
>
|
||||
{{ tag }}
|
||||
</v-chip>
|
||||
</div>
|
||||
|
||||
<h2 v-if="notes[0]" class="my-4">{{$t('recipe.notes')}}</h2>
|
||||
<v-card
|
||||
class="mt-1"
|
||||
v-for="(note, index) in notes"
|
||||
:key="generateKey('note', index)"
|
||||
>
|
||||
<v-card-title> {{ note.title }}</v-card-title>
|
||||
<v-card-text>
|
||||
{{ note.text }}
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-divider class="my-divider" :vertical="true"></v-divider>
|
||||
|
||||
<v-col cols="12" sm="12" md="8" lg="8">
|
||||
<h2 class="mb-4">{{$t('recipe.instructions')}}</h2>
|
||||
<v-hover
|
||||
v-for="(step, index) in instructions"
|
||||
:key="generateKey('step', index)"
|
||||
v-slot="{ hover }"
|
||||
>
|
||||
<v-card
|
||||
class="ma-1"
|
||||
:class="[{ 'on-hover': hover }, isDisabled(index)]"
|
||||
:elevation="hover ? 12 : 2"
|
||||
@click="toggleDisabled(index)"
|
||||
>
|
||||
<v-card-title>{{ $t('recipe.step-index', {step: index + 1}) }}</v-card-title>
|
||||
<v-card-text>{{ step.text }}</v-card-text>
|
||||
</v-card>
|
||||
</v-hover>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col></v-col>
|
||||
|
||||
<v-btn
|
||||
v-if="orgURL"
|
||||
dense
|
||||
small
|
||||
:hover="false"
|
||||
type="label"
|
||||
:ripple="false"
|
||||
elevation="0"
|
||||
:href="orgURL"
|
||||
color="secondary darken-1"
|
||||
target="_blank"
|
||||
class="rounded-sm mr-4"
|
||||
>
|
||||
{{$t('recipe.original-url')}}
|
||||
</v-btn>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import utils from "../../utils";
|
||||
export default {
|
||||
props: {
|
||||
name: String,
|
||||
description: String,
|
||||
ingredients: Array,
|
||||
instructions: Array,
|
||||
categories: Array,
|
||||
tags: Array,
|
||||
notes: Array,
|
||||
rating: Number,
|
||||
yields: String,
|
||||
orgURL: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
disabledSteps: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleDisabled(stepIndex) {
|
||||
if (this.disabledSteps.includes(stepIndex)) {
|
||||
let index = this.disabledSteps.indexOf(stepIndex);
|
||||
if (index !== -1) {
|
||||
this.disabledSteps.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
this.disabledSteps.push(stepIndex);
|
||||
}
|
||||
},
|
||||
isDisabled(stepIndex) {
|
||||
if (this.disabledSteps.includes(stepIndex)) {
|
||||
return "disabled-card";
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
generateKey(item, index) {
|
||||
return utils.generateUniqueKey(item, index);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.static {
|
||||
pointer-events: none;
|
||||
}
|
||||
.my-divider {
|
||||
margin: 0 -1px;
|
||||
}
|
||||
</style>
|
||||
34
frontend/src/components/Recipe/RecipeViewer/Ingredients.vue
Normal file
34
frontend/src/components/Recipe/RecipeViewer/Ingredients.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="mb-4">{{ $t("recipe.ingredients") }}</h2>
|
||||
<div
|
||||
v-for="(ingredient, index) in ingredients"
|
||||
:key="generateKey('ingredient', index)"
|
||||
>
|
||||
<v-checkbox
|
||||
hide-details
|
||||
class="ingredients"
|
||||
:label="ingredient"
|
||||
color="secondary"
|
||||
>
|
||||
</v-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import utils from "@/utils";
|
||||
export default {
|
||||
props: {
|
||||
ingredients: Array,
|
||||
},
|
||||
methods: {
|
||||
generateKey(item, index) {
|
||||
return utils.generateUniqueKey(item, index);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
36
frontend/src/components/Recipe/RecipeViewer/Notes.vue
Normal file
36
frontend/src/components/Recipe/RecipeViewer/Notes.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 v-if="notes[0]" class="my-4">{{ $t("recipe.notes") }}</h2>
|
||||
<v-card
|
||||
class="mt-1"
|
||||
v-for="(note, index) in notes"
|
||||
:key="generateKey('note', index)"
|
||||
>
|
||||
<v-card-title> {{ note.title }}</v-card-title>
|
||||
<v-card-text>
|
||||
<vue-markdown :source="note.text"> </vue-markdown>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VueMarkdown from "@adapttive/vue-markdown";
|
||||
import utils from "@/utils";
|
||||
export default {
|
||||
props: {
|
||||
notes: Array,
|
||||
},
|
||||
components: {
|
||||
VueMarkdown,
|
||||
},
|
||||
methods: {
|
||||
generateKey(item, index) {
|
||||
return utils.generateUniqueKey(item, index);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
26
frontend/src/components/Recipe/RecipeViewer/RecipeChips.vue
Normal file
26
frontend/src/components/Recipe/RecipeViewer/RecipeChips.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div v-if="items[0]">
|
||||
<h2 class="mt-4">{{ title }}</h2>
|
||||
<v-chip
|
||||
class="ma-1"
|
||||
color="accent"
|
||||
dark
|
||||
v-for="category in items"
|
||||
:key="category"
|
||||
>
|
||||
{{ category }}
|
||||
</v-chip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
items: Array,
|
||||
title: String,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
67
frontend/src/components/Recipe/RecipeViewer/Steps.vue
Normal file
67
frontend/src/components/Recipe/RecipeViewer/Steps.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="mb-4">{{ $t("recipe.instructions") }}</h2>
|
||||
<v-hover
|
||||
v-for="(step, index) in steps"
|
||||
:key="generateKey('step', index)"
|
||||
v-slot="{ hover }"
|
||||
>
|
||||
<v-card
|
||||
class="ma-1"
|
||||
:class="[{ 'on-hover': hover }, isDisabled(index)]"
|
||||
:elevation="hover ? 12 : 2"
|
||||
@click="toggleDisabled(index)"
|
||||
>
|
||||
<v-card-title>{{
|
||||
$t("recipe.step-index", { step: index + 1 })
|
||||
}}</v-card-title>
|
||||
<v-card-text>
|
||||
<vue-markdown :source="step.text"> </vue-markdown>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-hover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VueMarkdown from "@adapttive/vue-markdown";
|
||||
import utils from "@/utils";
|
||||
export default {
|
||||
props: {
|
||||
steps: Array,
|
||||
},
|
||||
components: {
|
||||
VueMarkdown,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
disabledSteps: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleDisabled(stepIndex) {
|
||||
if (this.disabledSteps.includes(stepIndex)) {
|
||||
let index = this.disabledSteps.indexOf(stepIndex);
|
||||
if (index !== -1) {
|
||||
this.disabledSteps.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
this.disabledSteps.push(stepIndex);
|
||||
}
|
||||
},
|
||||
isDisabled(stepIndex) {
|
||||
if (this.disabledSteps.includes(stepIndex)) {
|
||||
return "disabled-card";
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
generateKey(item, index) {
|
||||
return utils.generateUniqueKey(item, index);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
130
frontend/src/components/Recipe/RecipeViewer/index.vue
Normal file
130
frontend/src/components/Recipe/RecipeViewer/index.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-card-title class="headline">
|
||||
{{ name }}
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<vue-markdown :source="description"> </vue-markdown>
|
||||
<v-row dense disabled>
|
||||
<v-col>
|
||||
<v-btn
|
||||
v-if="yields"
|
||||
dense
|
||||
small
|
||||
:hover="false"
|
||||
type="label"
|
||||
:ripple="false"
|
||||
elevation="0"
|
||||
color="secondary darken-1"
|
||||
class="rounded-sm static"
|
||||
>
|
||||
{{ yields }}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-rating
|
||||
class="mr-2 align-end static"
|
||||
color="secondary darken-1"
|
||||
background-color="secondary lighten-3"
|
||||
length="5"
|
||||
:value="rating"
|
||||
></v-rating>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="4" lg="4">
|
||||
<Ingredients :ingredients="ingredients" />
|
||||
<div v-if="medium">
|
||||
<RecipeChips :title="$t('recipe.categories')" :items="categories" />
|
||||
<RecipeChips :title="$t('recipe.tags')" :items="tags" />
|
||||
<Notes :notes="notes" />
|
||||
</div>
|
||||
</v-col>
|
||||
<v-divider
|
||||
v-if="medium"
|
||||
class="my-divider"
|
||||
:vertical="true"
|
||||
></v-divider>
|
||||
|
||||
<v-col cols="12" sm="12" md="8" lg="8">
|
||||
<Steps :steps="instructions" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<div v-if="!medium">
|
||||
<RecipeChips :title="$t('recipe.categories')" :items="categories" />
|
||||
<RecipeChips :title="$t('recipe.tags')" :items="tags" />
|
||||
<Notes :notes="notes" />
|
||||
</div>
|
||||
<v-row class="mt-2 mb-1">
|
||||
<v-col></v-col>
|
||||
<v-btn
|
||||
v-if="orgURL"
|
||||
dense
|
||||
small
|
||||
:hover="false"
|
||||
type="label"
|
||||
:ripple="false"
|
||||
elevation="0"
|
||||
:href="orgURL"
|
||||
color="secondary darken-1"
|
||||
target="_blank"
|
||||
class="rounded-sm mr-4"
|
||||
>
|
||||
{{ $t("recipe.original-url") }}
|
||||
</v-btn>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VueMarkdown from "@adapttive/vue-markdown";
|
||||
import utils from "@/utils";
|
||||
import RecipeChips from "./RecipeChips";
|
||||
import Steps from "./Steps";
|
||||
import Notes from "./Notes";
|
||||
import Ingredients from "./Ingredients";
|
||||
export default {
|
||||
components: {
|
||||
VueMarkdown,
|
||||
RecipeChips,
|
||||
Steps,
|
||||
Notes,
|
||||
Ingredients,
|
||||
},
|
||||
props: {
|
||||
name: String,
|
||||
description: String,
|
||||
ingredients: Array,
|
||||
instructions: Array,
|
||||
categories: Array,
|
||||
tags: Array,
|
||||
notes: Array,
|
||||
rating: Number,
|
||||
yields: String,
|
||||
orgURL: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
disabledSteps: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
medium() {
|
||||
return this.$vuetify.breakpoint.mdAndUp;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
generateKey(item, index) {
|
||||
return utils.generateUniqueKey(item, index);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.static {
|
||||
pointer-events: none;
|
||||
}
|
||||
.my-divider {
|
||||
margin: 0 -1px;
|
||||
}
|
||||
</style>
|
||||
@@ -9,6 +9,7 @@
|
||||
/>
|
||||
<v-row>
|
||||
<v-col
|
||||
:cols="12"
|
||||
:sm="6"
|
||||
:md="6"
|
||||
:lg="4"
|
||||
@@ -19,14 +20,14 @@
|
||||
<v-card hover outlined @click="openDialog(backup)">
|
||||
<v-card-text>
|
||||
<v-row align="center">
|
||||
<v-col cols="12" sm="2">
|
||||
<v-icon large color="primary"> mdi-backup-restore </v-icon>
|
||||
<v-col cols="2">
|
||||
<v-icon large color="primary">mdi-backup-restore</v-icon>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="10">
|
||||
<div>
|
||||
<v-col cols="10">
|
||||
<div class="text-truncate">
|
||||
<strong>{{ backup.name }}</strong>
|
||||
</div>
|
||||
<div>{{ readableTime(backup.date) }}</div>
|
||||
<div class="text-truncate">{{ readableTime(backup.date) }}</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
@@ -38,8 +39,8 @@
|
||||
|
||||
<script>
|
||||
import ImportDialog from "./ImportDialog";
|
||||
import api from "../../../api";
|
||||
import utils from "../../../utils";
|
||||
import api from "@/api";
|
||||
import utils from "@/utils";
|
||||
export default {
|
||||
props: {
|
||||
backups: Array,
|
||||
@@ -68,10 +69,9 @@ export default {
|
||||
this.$emit("loading");
|
||||
let response = await api.backups.import(data.name, data);
|
||||
|
||||
let failed = response.data.failed;
|
||||
let succesful = response.data.successful;
|
||||
let importData = response.data;
|
||||
|
||||
this.$emit("finished", succesful, failed);
|
||||
this.$emit("finished", importData);
|
||||
},
|
||||
deleteBackup(data) {
|
||||
this.$emit("loading");
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
|
||||
<script>
|
||||
import ImportDialog from "./ImportDialog";
|
||||
import api from "../../../api";
|
||||
import utils from "../../../utils";
|
||||
import api from "@/api";
|
||||
import utils from "@/utils";
|
||||
export default {
|
||||
props: {
|
||||
backups: Array,
|
||||
|
||||
@@ -1,7 +1,23 @@
|
||||
<template>
|
||||
<div class="text-center">
|
||||
<v-dialog v-model="dialog" width="500">
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
width="500"
|
||||
:fullscreen="$vuetify.breakpoint.xsOnly"
|
||||
>
|
||||
<v-card>
|
||||
<v-toolbar dark color="primary" v-show="$vuetify.breakpoint.xsOnly">
|
||||
<v-btn icon dark @click="dialog = false">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
<v-toolbar-title></v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-toolbar-items>
|
||||
<v-btn dark text @click="raiseEvent('import')">
|
||||
{{ $t("general.import") }}
|
||||
</v-btn>
|
||||
</v-toolbar-items>
|
||||
</v-toolbar>
|
||||
<v-card-title> {{ name }} </v-card-title>
|
||||
<v-card-subtitle class="mb-n3"> {{ date }} </v-card-subtitle>
|
||||
<v-divider></v-divider>
|
||||
@@ -72,7 +88,12 @@
|
||||
<v-btn color="error" text @click="raiseEvent('delete')">
|
||||
{{ $t("general.delete") }}
|
||||
</v-btn>
|
||||
<v-btn color="success" outlined @click="raiseEvent('import')">
|
||||
<v-btn
|
||||
color="success"
|
||||
outlined
|
||||
@click="raiseEvent('import')"
|
||||
v-show="$vuetify.breakpoint.smAndUp"
|
||||
>
|
||||
{{ $t("general.import") }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-data-table
|
||||
dense
|
||||
:headers="dataHeaders"
|
||||
:items="dataSet"
|
||||
item-key="name"
|
||||
class="elevation-1 mt-2"
|
||||
show-expand
|
||||
:expanded.sync="expanded"
|
||||
:footer-props="{
|
||||
'items-per-page-options': [100, 200, 300, 400, -1],
|
||||
}"
|
||||
:items-per-page="100"
|
||||
>
|
||||
<template v-slot:item.status="{ item }">
|
||||
<div :class="item.status ? 'success--text' : 'error--text'">
|
||||
{{ item.status ? "Imported" : "Failed" }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:expanded-item="{ headers, item }">
|
||||
<td :colspan="headers.length">
|
||||
<div class="ma-2">
|
||||
{{ item.exception }}
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
dataSet: Array,
|
||||
dataHeaders: Array,
|
||||
},
|
||||
data: () => ({
|
||||
singleExpand: false,
|
||||
expanded: [],
|
||||
}),
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,152 @@
|
||||
<template>
|
||||
<div class="text-center">
|
||||
<v-dialog v-model="dialog" width="70%">
|
||||
<v-card>
|
||||
<v-card-title> Import Summary </v-card-title>
|
||||
<v-card-text>
|
||||
<v-row class="mb-n9">
|
||||
<v-card flat>
|
||||
<v-card-text>
|
||||
<div>
|
||||
<h3>Recipes</h3>
|
||||
</div>
|
||||
<div class="success--text">
|
||||
Success: {{ recipeNumbers.success }}
|
||||
</div>
|
||||
<div class="error--text">
|
||||
Failed: {{ recipeNumbers.failure }}
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<v-card flat>
|
||||
<v-card-text>
|
||||
<div>
|
||||
<h3>Themes</h3>
|
||||
</div>
|
||||
<div class="success--text">
|
||||
Success: {{ themeNumbers.success }}
|
||||
</div>
|
||||
<div class="error--text">
|
||||
Failed: {{ themeNumbers.failure }}
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<v-card flat>
|
||||
<v-card-text>
|
||||
<div>
|
||||
<h3>Settings</h3>
|
||||
</div>
|
||||
<div class="success--text">
|
||||
Success: {{ settingsNumbers.success }}
|
||||
</div>
|
||||
<div class="error--text">
|
||||
Failed: {{ settingsNumbers.failure }}
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-tabs v-model="tab">
|
||||
<v-tab>Recipes</v-tab>
|
||||
<v-tab>Themes</v-tab>
|
||||
<v-tab>Settings</v-tab>
|
||||
</v-tabs>
|
||||
<v-tabs-items v-model="tab">
|
||||
<v-tab-item>
|
||||
<v-card flat>
|
||||
<DataTable :data-headers="recipeHeaders" :data-set="recipeData" />
|
||||
</v-card>
|
||||
</v-tab-item>
|
||||
<v-tab-item>
|
||||
<v-card>
|
||||
<DataTable
|
||||
:data-headers="recipeHeaders"
|
||||
:data-set="themeData"
|
||||
/> </v-card
|
||||
></v-tab-item>
|
||||
<v-tab-item>
|
||||
<v-card
|
||||
><DataTable
|
||||
:data-headers="recipeHeaders"
|
||||
:data-set="settingsData"
|
||||
/>
|
||||
</v-card>
|
||||
</v-tab-item>
|
||||
</v-tabs-items>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DataTable from "./DataTable";
|
||||
export default {
|
||||
components: {
|
||||
DataTable,
|
||||
},
|
||||
data: () => ({
|
||||
tab: null,
|
||||
dialog: false,
|
||||
recipeData: [],
|
||||
themeData: [],
|
||||
settingsData: [],
|
||||
recipeHeaders: [
|
||||
{
|
||||
text: "Status",
|
||||
value: "status",
|
||||
},
|
||||
{
|
||||
text: "Name",
|
||||
align: "start",
|
||||
sortable: true,
|
||||
value: "name",
|
||||
},
|
||||
|
||||
{ text: "Exception", value: "data-table-expand", align: "center" },
|
||||
],
|
||||
allDataTables: [],
|
||||
}),
|
||||
|
||||
computed: {
|
||||
recipeNumbers() {
|
||||
let numbers = { success: 0, failure: 0 };
|
||||
this.recipeData.forEach(element => {
|
||||
if (element.status) {
|
||||
numbers.success++;
|
||||
} else numbers.failure++;
|
||||
});
|
||||
return numbers;
|
||||
},
|
||||
settingsNumbers() {
|
||||
let numbers = { success: 0, failure: 0 };
|
||||
this.settingsData.forEach(element => {
|
||||
if (element.status) {
|
||||
numbers.success++;
|
||||
} else numbers.failure++;
|
||||
});
|
||||
return numbers;
|
||||
},
|
||||
themeNumbers() {
|
||||
let numbers = { success: 0, failure: 0 };
|
||||
this.themeData.forEach(element => {
|
||||
if (element.status) {
|
||||
numbers.success++;
|
||||
} else numbers.failure++;
|
||||
});
|
||||
return numbers;
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
open(importData) {
|
||||
this.recipeData = importData.recipeImports;
|
||||
this.themeData = importData.themeReport;
|
||||
this.settingsData = importData.settingsReport;
|
||||
this.dialog = true;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -8,7 +8,7 @@
|
||||
v-model="tag"
|
||||
></v-text-field>
|
||||
</v-card-text>
|
||||
<v-card-actions class="mt-n9">
|
||||
<v-card-actions class="mt-n9 flex-wrap">
|
||||
<v-switch v-model="fullBackup" :label="switchLabel"></v-switch>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="success" text @click="createBackup()">
|
||||
@@ -46,7 +46,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../../../api";
|
||||
import api from "@/api";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
@@ -84,7 +84,7 @@ export default {
|
||||
methods: {
|
||||
async getAvailableBackups() {
|
||||
let response = await api.backups.requestAvailable();
|
||||
response.templates.forEach((element) => {
|
||||
response.templates.forEach(element => {
|
||||
this.availableTemplates.push(element);
|
||||
});
|
||||
},
|
||||
@@ -101,7 +101,6 @@ export default {
|
||||
templates: this.selectedTemplates,
|
||||
};
|
||||
|
||||
|
||||
await api.backups.create(data);
|
||||
this.loading = false;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6" ss="12">
|
||||
<v-col cols="12" md="6" sm="12">
|
||||
<NewBackupCard @created="processFinished" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="6" sm="12">
|
||||
@@ -41,13 +41,15 @@
|
||||
:failed-header="$t('settings.backup.failed-imports')"
|
||||
:failed="failedImports"
|
||||
/>
|
||||
<ImportSummaryDialog ref="report" :import-data="importData" />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../../../api";
|
||||
import api from "@/api";
|
||||
import SuccessFailureAlert from "../../UI/SuccessFailureAlert";
|
||||
import ImportSummaryDialog from "./ImportSummaryDialog";
|
||||
import UploadBtn from "../../UI/UploadBtn";
|
||||
import AvailableBackupCard from "./AvailableBackupCard";
|
||||
import NewBackupCard from "./NewBackupCard";
|
||||
@@ -58,6 +60,7 @@ export default {
|
||||
UploadBtn,
|
||||
AvailableBackupCard,
|
||||
NewBackupCard,
|
||||
ImportSummaryDialog,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -65,6 +68,7 @@ export default {
|
||||
successfulImports: [],
|
||||
backupLoading: false,
|
||||
availableBackups: [],
|
||||
importData: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
@@ -87,12 +91,10 @@ export default {
|
||||
this.backupLoading = false;
|
||||
}
|
||||
},
|
||||
processFinished(successful = null, failed = null) {
|
||||
processFinished(data) {
|
||||
this.getAvailableBackups();
|
||||
this.backupLoading = false;
|
||||
this.successfulImports = successful;
|
||||
this.failedImports = failed;
|
||||
this.$refs.report.open();
|
||||
this.$refs.report.open(data);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
<v-card-text>
|
||||
<h2 class="mt-1 mb-1">Home Page</h2>
|
||||
<v-row align="center" justify="center" dense class="mb-n7 pb-n5">
|
||||
<v-col sm="2">
|
||||
<v-col cols="12" sm="3" md="2">
|
||||
<v-switch v-model="showRecent" label="Show Recent"></v-switch>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-col cols="12" sm="5" md="5">
|
||||
<v-slider
|
||||
class="pt-4"
|
||||
class="pt-sm-4"
|
||||
label="Card Per Section"
|
||||
v-model="showLimit"
|
||||
max="30"
|
||||
@@ -24,7 +24,7 @@
|
||||
</v-card-text>
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-card outlined min-height="250">
|
||||
<v-card-text class="pt-2 pb-1">
|
||||
<h3>Homepage Categories</h3>
|
||||
@@ -64,7 +64,7 @@
|
||||
</v-list>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-card outlined min-height="250px">
|
||||
<v-card-text class="pt-2 pb-1">
|
||||
<h3>
|
||||
@@ -126,7 +126,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../../../api";
|
||||
import api from "@/api";
|
||||
import draggable from "vuedraggable";
|
||||
|
||||
export default {
|
||||
|
||||
@@ -11,12 +11,10 @@
|
||||
</span>
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<HomePageSettings />
|
||||
<v-divider></v-divider>
|
||||
<v-card-text>
|
||||
<h2 class="mt-1 mb-4">{{ $t("settings.language") }}</h2>
|
||||
<v-row>
|
||||
<v-col cols="3">
|
||||
<v-col sm="3">
|
||||
<v-select
|
||||
dense
|
||||
v-model="selectedLang"
|
||||
@@ -30,6 +28,8 @@
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-divider></v-divider>
|
||||
<HomePageSettings />
|
||||
<v-divider></v-divider>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
@@ -59,6 +59,9 @@ export default {
|
||||
this.langOptions = this.$store.getters.getAllLangs;
|
||||
this.selectedLang = this.$store.getters.getActiveLang;
|
||||
},
|
||||
removeCategory(index) {
|
||||
this.value.categories.splice(index, 1);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,49 @@
|
||||
<template>
|
||||
<v-card>
|
||||
<v-card-title class="headline">
|
||||
{{ $t("settings.webhooks.meal-planner-webhooks") }}
|
||||
{{ $t("meal-plan.meal-planner") }}
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<v-card-text>
|
||||
<h2 class="mt-1">{{ $t("recipe.categories") }}</h2>
|
||||
|
||||
<v-row>
|
||||
<v-col sm="12" md="6">
|
||||
<v-select
|
||||
outlined
|
||||
:flat="isFlat"
|
||||
elavation="0"
|
||||
v-model="planCategories"
|
||||
:items="categories"
|
||||
item-text="name"
|
||||
item-value="name"
|
||||
multiple
|
||||
chips
|
||||
hint="Only recipes with these categories will be used in Meal Plans"
|
||||
class="mt-2"
|
||||
persistent-hint
|
||||
>
|
||||
<template v-slot:selection="data">
|
||||
<v-chip
|
||||
outlined
|
||||
:input-value="data.selected"
|
||||
close
|
||||
@click:close="removeCategory(data.index)"
|
||||
color="secondary"
|
||||
dark
|
||||
>
|
||||
{{ data.item.name }}
|
||||
</v-chip>
|
||||
</template>
|
||||
</v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-divider> </v-divider>
|
||||
<v-card-text>
|
||||
<h2 class="mt-1 mb-4">
|
||||
{{ $t("settings.webhooks.meal-planner-webhooks") }}
|
||||
</h2>
|
||||
<p>
|
||||
{{
|
||||
$t(
|
||||
@@ -56,7 +96,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../../../api";
|
||||
import api from "@/api";
|
||||
import TimePickerDialog from "./TimePickerDialog";
|
||||
export default {
|
||||
components: {
|
||||
@@ -68,11 +108,20 @@ export default {
|
||||
webhooks: [],
|
||||
enabled: false,
|
||||
time: "",
|
||||
planCategories: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getSiteSettings();
|
||||
},
|
||||
computed: {
|
||||
categories() {
|
||||
return this.$store.getters.getCategories;
|
||||
},
|
||||
isFlat() {
|
||||
return this.planCategories ? true : false;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
saveTime(value) {
|
||||
this.time = value;
|
||||
@@ -83,6 +132,7 @@ export default {
|
||||
this.name = settings.name;
|
||||
this.time = settings.webhooks.webhookTime;
|
||||
this.enabled = settings.webhooks.enabled;
|
||||
this.planCategories = settings.planCategories;
|
||||
},
|
||||
addWebhook() {
|
||||
this.webhooks.push(" ");
|
||||
@@ -93,6 +143,7 @@ export default {
|
||||
saveWebhooks() {
|
||||
const body = {
|
||||
name: this.name,
|
||||
planCategories: this.planCategories,
|
||||
webhooks: {
|
||||
webhookURLs: this.webhooks,
|
||||
webhookTime: this.time,
|
||||
@@ -104,6 +155,9 @@ export default {
|
||||
testWebhooks() {
|
||||
api.settings.testWebhooks();
|
||||
},
|
||||
removeCategory(index) {
|
||||
this.planCategories.splice(index, 1);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -21,14 +21,16 @@
|
||||
>
|
||||
<v-card-text>
|
||||
<v-row align="center">
|
||||
<v-col cols="12" sm="2">
|
||||
<v-icon large color="primary"> mdi-import </v-icon>
|
||||
<v-col cols="2">
|
||||
<v-icon large color="primary">mdi-import</v-icon>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="10">
|
||||
<div>
|
||||
<v-col cols="10">
|
||||
<div class="text-truncate">
|
||||
<strong>{{ migration.name }}</strong>
|
||||
</div>
|
||||
<div>{{ readableTime(migration.date) }}</div>
|
||||
<div class="text-truncate">
|
||||
{{ readableTime(migration.date) }}
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
@@ -56,8 +58,8 @@
|
||||
|
||||
<script>
|
||||
import UploadBtn from "../../UI/UploadBtn";
|
||||
import utils from "../../../utils";
|
||||
import api from "../../../api";
|
||||
import utils from "@/utils";
|
||||
import api from "@/api";
|
||||
export default {
|
||||
props: {
|
||||
folder: String,
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
|
||||
<v-row dense>
|
||||
<v-col
|
||||
:sm="12"
|
||||
:cols="12"
|
||||
:sm="6"
|
||||
:md="6"
|
||||
:lg="4"
|
||||
:xl="3"
|
||||
@@ -41,7 +42,7 @@
|
||||
<script>
|
||||
import MigrationCard from "./MigrationCard";
|
||||
import SuccessFailureAlert from "../../UI/SuccessFailureAlert";
|
||||
import api from "../../../api";
|
||||
import api from "@/api";
|
||||
export default {
|
||||
components: {
|
||||
MigrationCard,
|
||||
@@ -78,7 +79,7 @@ export default {
|
||||
},
|
||||
async getAvailableMigrations() {
|
||||
let response = await api.migrations.getMigrations();
|
||||
response.forEach((element) => {
|
||||
response.forEach(element => {
|
||||
if (element.type === "nextcloud") {
|
||||
this.migrations.nextcloud.availableImports = element.files;
|
||||
} else if (element.type === "chowdown") {
|
||||
|
||||
@@ -3,7 +3,13 @@
|
||||
<div class="text-center">
|
||||
<h3>{{ buttonText }}</h3>
|
||||
</div>
|
||||
<v-text-field v-model="color" hide-details class="ma-0 pa-0" solo>
|
||||
<v-text-field
|
||||
v-model="color"
|
||||
hide-details
|
||||
class="ma-0 pa-0"
|
||||
solo
|
||||
v-show="$vuetify.breakpoint.mdAndUp"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<v-menu
|
||||
v-model="menu"
|
||||
@@ -17,12 +23,27 @@
|
||||
</template>
|
||||
<v-card>
|
||||
<v-card-text class="pa-0">
|
||||
<v-color-picker v-model="color" flat show-swatches />
|
||||
<v-color-picker v-model="color" flat mode="hexa" show-swatches />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-text-field>
|
||||
<div class="text-center" v-show="$vuetify.breakpoint.smAndDown">
|
||||
<v-menu
|
||||
v-model="menu"
|
||||
top
|
||||
nudge-bottom="105"
|
||||
nudge-left="16"
|
||||
:close-on-content-click="false"
|
||||
>
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-chip label :color="`${color}`" dark v-bind="attrs" v-on="on">
|
||||
{{ color }}
|
||||
</v-chip>
|
||||
</template>
|
||||
</v-menu>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -36,7 +57,7 @@ export default {
|
||||
return {
|
||||
dialog: false,
|
||||
swatches: false,
|
||||
color: "#1976D2",
|
||||
color: this.value || "#1976D2",
|
||||
mask: "!#XXXXXXXX",
|
||||
menu: false,
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
}}
|
||||
</p>
|
||||
<v-row dense align="center">
|
||||
<v-col cols="12">
|
||||
<v-col cols="6">
|
||||
<v-btn-toggle
|
||||
v-model="selectedDarkMode"
|
||||
color="primary "
|
||||
@@ -22,12 +22,25 @@
|
||||
@change="setStoresDarkMode"
|
||||
>
|
||||
<v-btn value="system">
|
||||
{{ $t("settings.theme.default-to-system") }}
|
||||
<v-icon>mdi-desktop-tower-monitor</v-icon>
|
||||
<span class="ml-1" v-show="$vuetify.breakpoint.smAndUp">
|
||||
{{ $t("settings.theme.default-to-system") }}
|
||||
</span>
|
||||
</v-btn>
|
||||
|
||||
<v-btn value="light"> {{ $t("settings.theme.light") }} </v-btn>
|
||||
<v-btn value="light">
|
||||
<v-icon>mdi-white-balance-sunny</v-icon>
|
||||
<span class="ml-1" v-show="$vuetify.breakpoint.smAndUp">
|
||||
{{ $t("settings.theme.light") }}
|
||||
</span>
|
||||
</v-btn>
|
||||
|
||||
<v-btn value="dark"> {{ $t("settings.theme.dark") }} </v-btn>
|
||||
<v-btn value="dark">
|
||||
<v-icon>mdi-weather-night</v-icon>
|
||||
<span class="ml-1" v-show="$vuetify.breakpoint.smAndUp">
|
||||
{{ $t("settings.theme.dark") }}
|
||||
</span>
|
||||
</v-btn>
|
||||
</v-btn-toggle>
|
||||
</v-col>
|
||||
</v-row></v-card-text
|
||||
@@ -45,7 +58,7 @@
|
||||
|
||||
<v-form ref="form" lazy-validation>
|
||||
<v-row dense align="center">
|
||||
<v-col md="4" sm="3">
|
||||
<v-col cols="12" md="4" sm="3">
|
||||
<v-select
|
||||
:label="$t('settings.theme.saved-color-theme')"
|
||||
:items="availableThemes"
|
||||
@@ -53,7 +66,7 @@
|
||||
return-object
|
||||
v-model="selectedTheme"
|
||||
@change="themeSelected"
|
||||
:rules="[(v) => !!v || $t('settings.theme.theme-is-required')]"
|
||||
:rules="[v => !!v || $t('settings.theme.theme-is-required')]"
|
||||
required
|
||||
>
|
||||
</v-select>
|
||||
@@ -136,7 +149,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../../../api";
|
||||
import api from "@/api";
|
||||
import ColorPickerDialog from "./ColorPickerDialog";
|
||||
import NewThemeDialog from "./NewThemeDialog";
|
||||
import Confirmation from "../../UI/Confirmation";
|
||||
@@ -186,7 +199,7 @@ export default {
|
||||
//Change to default if deleting current theme.
|
||||
if (
|
||||
!this.availableThemes.some(
|
||||
(theme) => theme.name === this.selectedTheme.name
|
||||
theme => theme.name === this.selectedTheme.name
|
||||
)
|
||||
) {
|
||||
await this.$store.dispatch("resetTheme");
|
||||
|
||||
@@ -7,10 +7,14 @@
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<v-form>
|
||||
<v-form ref="urlForm">
|
||||
<v-text-field
|
||||
v-model="recipeURL"
|
||||
:label="$t('new-recipe.recipe-url')"
|
||||
required
|
||||
validate-on-blur
|
||||
autofocus
|
||||
:rules="[isValidWebUrl]"
|
||||
></v-text-field>
|
||||
</v-form>
|
||||
|
||||
@@ -49,7 +53,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../../api";
|
||||
import api from "@/api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@@ -64,18 +68,20 @@ export default {
|
||||
|
||||
methods: {
|
||||
async createRecipe() {
|
||||
this.processing = true;
|
||||
let response = await api.recipes.createByURL(this.recipeURL);
|
||||
if (response.status !== 201) {
|
||||
this.error = true;
|
||||
this.processing = false;
|
||||
return;
|
||||
}
|
||||
if (this.$refs.urlForm.validate()) {
|
||||
this.processing = true;
|
||||
let response = await api.recipes.createByURL(this.recipeURL);
|
||||
if (response.status !== 201) {
|
||||
this.error = true;
|
||||
this.processing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.addRecipe = false;
|
||||
this.processing = false;
|
||||
this.recipeURL = "";
|
||||
this.$router.push(`/recipe/${response.data}`);
|
||||
this.addRecipe = false;
|
||||
this.processing = false;
|
||||
this.recipeURL = "";
|
||||
this.$router.push(`/recipe/${response.data}`);
|
||||
}
|
||||
},
|
||||
|
||||
navCreate() {
|
||||
@@ -89,6 +95,10 @@ export default {
|
||||
this.recipeURL = "";
|
||||
this.processing = false;
|
||||
},
|
||||
isValidWebUrl(url) {
|
||||
let regEx = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)$/gm;
|
||||
return regEx.test(url) ? true : "Must be a Valid URL";
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,20 +1,43 @@
|
||||
<template>
|
||||
<v-navigation-drawer width="175px" clipped app permanent expand-on-hover>
|
||||
<v-list nav dense>
|
||||
<v-list-item v-for="nav in links" :key="nav.title" link :to="nav.to">
|
||||
<v-list-item-icon>
|
||||
<v-icon>{{ nav.icon }}</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title>{{ nav.title | titleCase }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
<div>
|
||||
<v-btn
|
||||
class="mt-9 ml-n1"
|
||||
fixed
|
||||
left
|
||||
bottom
|
||||
fab
|
||||
small
|
||||
color="primary"
|
||||
@click="showSidebar = !showSidebar"
|
||||
>
|
||||
<v-icon>mdi-tag</v-icon></v-btn
|
||||
>
|
||||
|
||||
<v-navigation-drawer
|
||||
:value="mobile ? showSidebar : true"
|
||||
v-model="showSidebar"
|
||||
width="175px"
|
||||
clipped
|
||||
app
|
||||
>
|
||||
<v-list nav dense>
|
||||
<v-list-item v-for="nav in links" :key="nav.title" link :to="nav.to">
|
||||
<v-list-item-icon>
|
||||
<v-icon>{{ nav.icon }}</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title>{{ nav.title | titleCase }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
showSidebar: false,
|
||||
mobile: false,
|
||||
links: [],
|
||||
baseLinks: [
|
||||
{
|
||||
@@ -39,16 +62,20 @@ export default {
|
||||
allCategories() {
|
||||
this.buildSidebar();
|
||||
},
|
||||
showSidebar() {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.buildSidebar();
|
||||
this.mobile = this.viewScale();
|
||||
this.showSidebar = !this.viewScale();
|
||||
},
|
||||
|
||||
methods: {
|
||||
async buildSidebar() {
|
||||
this.links = [];
|
||||
this.links.push(...this.baseLinks);
|
||||
this.allCategories.forEach(async (element) => {
|
||||
this.allCategories.forEach(async element => {
|
||||
this.links.push({
|
||||
title: element.name,
|
||||
to: `/recipes/${element.slug}`,
|
||||
@@ -56,6 +83,16 @@ export default {
|
||||
});
|
||||
});
|
||||
},
|
||||
viewScale() {
|
||||
switch (this.$vuetify.breakpoint.name) {
|
||||
case "xs":
|
||||
return true;
|
||||
case "sm":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -44,14 +44,14 @@
|
||||
color="primary"
|
||||
block="block"
|
||||
type="submit"
|
||||
>{{$t('login.sign-in')}}</v-btn
|
||||
>{{ $t("login.sign-in") }}</v-btn
|
||||
>
|
||||
<v-btn
|
||||
v-else
|
||||
block="block"
|
||||
type="submit"
|
||||
@click.prevent="options.isLoggingIn = true"
|
||||
>{{$t('login.sign-up')}}</v-btn
|
||||
>{{ $t("login.sign-up") }}</v-btn
|
||||
>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
@@ -72,7 +72,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../../api";
|
||||
import api from "@/api";
|
||||
export default {
|
||||
props: {},
|
||||
data() {
|
||||
|
||||
@@ -6,8 +6,24 @@
|
||||
:to="route ? `/recipe/${slug}` : ''"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
<v-img height="200" :src="getImage(image)"></v-img>
|
||||
<v-card-title class="my-n3 mb-n6">{{ name | truncate(30) }}</v-card-title>
|
||||
<v-img height="200" :src="getImage(image)">
|
||||
<v-expand-transition v-if="description">
|
||||
<div
|
||||
v-if="hover"
|
||||
class="d-flex transition-fast-in-fast-out secondary v-card--reveal "
|
||||
style="height: 100%;"
|
||||
>
|
||||
<v-card-text class="v-card--text-show white--text">
|
||||
{{ description | truncate(300) }}
|
||||
</v-card-text>
|
||||
</div>
|
||||
</v-expand-transition>
|
||||
</v-img>
|
||||
<v-card-title class="my-n3 mb-n6 ">
|
||||
<div class="headerClass">
|
||||
{{ name }}
|
||||
</div>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-actions class="">
|
||||
<v-row dense align="center">
|
||||
@@ -23,16 +39,7 @@
|
||||
></v-rating>
|
||||
</v-col>
|
||||
<v-col></v-col>
|
||||
<v-col align="end">
|
||||
<v-tooltip top color="secondary" max-width="400" open-delay="50">
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-btn color="secondary" v-on="on" v-bind="attrs" text>{{
|
||||
$t("recipe.description")
|
||||
}}</v-btn>
|
||||
</template>
|
||||
<span>{{ description }}</span>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
<v-col align="end"> </v-col>
|
||||
</v-row>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
@@ -40,7 +47,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import utils from "../../utils";
|
||||
import utils from "@/utils";
|
||||
export default {
|
||||
props: {
|
||||
name: String,
|
||||
@@ -61,4 +68,21 @@ export default {
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.v-card--reveal {
|
||||
align-items: center;
|
||||
bottom: 0;
|
||||
justify-content: center;
|
||||
opacity: 0.8;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
.v-card--text-show {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
.headerClass {
|
||||
white-space: nowrap;
|
||||
word-break: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
@@ -12,6 +12,8 @@
|
||||
hide-no-data
|
||||
cache-items
|
||||
solo
|
||||
autofocus
|
||||
auto-select-first
|
||||
>
|
||||
<template
|
||||
v-if="showResults"
|
||||
@@ -43,7 +45,7 @@
|
||||
|
||||
<script>
|
||||
import Fuse from "fuse.js";
|
||||
import utils from "../../utils";
|
||||
import utils from "@/utils";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -55,6 +57,7 @@ export default {
|
||||
return {
|
||||
searchSlug: "",
|
||||
search: " ",
|
||||
data: [],
|
||||
result: [],
|
||||
autoResults: [],
|
||||
isDark: false,
|
||||
@@ -65,27 +68,30 @@ export default {
|
||||
distance: 100,
|
||||
maxPatternLength: 32,
|
||||
minMatchCharLength: 1,
|
||||
keys: ["name", "slug"],
|
||||
keys: ["name", "slug", "description"],
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.isDark = this.$store.getters.getIsDark;
|
||||
this.data = this.$store.getters.getRecentRecipes;
|
||||
},
|
||||
computed: {
|
||||
data() {
|
||||
return this.$store.getters.getRecentRecipes;
|
||||
},
|
||||
fuse() {
|
||||
return new Fuse(this.data, this.options);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
search() {
|
||||
if (this.search.trim() === "") this.result = this.list;
|
||||
else this.result = this.fuse.search(this.search.trim());
|
||||
|
||||
try {
|
||||
this.result = this.fuse.search(this.search.trim());
|
||||
} catch {
|
||||
this.result = this.data
|
||||
.map(x => ({ item: x }))
|
||||
.sort((a, b) => (a.name > b.name ? 1 : -1));
|
||||
}
|
||||
this.$emit("results", this.result);
|
||||
|
||||
if (this.showResults === true) {
|
||||
this.autoResults = this.result;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="text-center">
|
||||
<v-dialog v-model="dialog" min-height="700" max-width="1000">
|
||||
<v-dialog v-model="dialog" height="100%" max-width="1200">
|
||||
<v-card min-height="725" height="100%">
|
||||
<v-card-text>
|
||||
<v-card-title></v-card-title>
|
||||
@@ -22,7 +22,7 @@
|
||||
:md="6"
|
||||
:lg="4"
|
||||
:xl="3"
|
||||
v-for="item in searchResults.slice(0, 10)"
|
||||
v-for="item in searchResults.slice(0, 24)"
|
||||
:key="item.item.name"
|
||||
>
|
||||
<RecipeCard
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
<template>
|
||||
<v-dialog v-model="dialog" max-width="900px">
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
max-width="900px"
|
||||
:fullscreen="$vuetify.breakpoint.xsOnly"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title> {{ title }} </v-card-title>
|
||||
<v-toolbar dark color="primary" v-show="$vuetify.breakpoint.xsOnly">
|
||||
<v-btn icon dark @click="dialog = false">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
<v-toolbar-title>{{ title }}</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-toolbar-items></v-toolbar-items>
|
||||
</v-toolbar>
|
||||
<v-card-title v-show="$vuetify.breakpoint.smAndUp">
|
||||
{{ title }}
|
||||
</v-card-title>
|
||||
<v-card-text class="mt-3">
|
||||
<v-row>
|
||||
<v-col>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../../api";
|
||||
import api from "@/api";
|
||||
export default {
|
||||
props: {
|
||||
url: String,
|
||||
|
||||
158
frontend/src/locales/de.json
Normal file
158
frontend/src/locales/de.json
Normal file
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"404": {
|
||||
"page-not-found": "404 Seite nicht gefunden",
|
||||
"take-me-home": "Zurück"
|
||||
},
|
||||
"new-recipe": {
|
||||
"from-url": "Von URL",
|
||||
"recipe-url": "Rezept URL",
|
||||
"error-message": "Ein Fehler ist beim import der URL aufgetreten. Überprüfe das Log sowie debug/last_recipe.json um zu sehen was schief gelaufen ist.",
|
||||
"bulk-add": "Massenimport",
|
||||
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Füge deine Rezeptdaten ein. Jede Zeile wird als Eintrag in einer Liste dargestellt"
|
||||
},
|
||||
"general": {
|
||||
"upload": "Hochladen",
|
||||
"submit": "Einfügen",
|
||||
"name": "Name",
|
||||
"settings": "Einstellungen",
|
||||
"close": "Schließen",
|
||||
"save": "Speichern",
|
||||
"image-file": "Bilddatei",
|
||||
"update": "Aktualisieren",
|
||||
"edit": "Bearbeiten",
|
||||
"delete": "Löschen",
|
||||
"select": "Auswählen",
|
||||
"random": "Zufall",
|
||||
"new": "Neu",
|
||||
"create": "Erstellen",
|
||||
"cancel": "Abbrechen",
|
||||
"ok": "OK",
|
||||
"enabled": "Aktiviert",
|
||||
"download": "Herunterladen",
|
||||
"import": "Importieren",
|
||||
"options": "Optionen",
|
||||
"templates": "Vorlagen",
|
||||
"recipes": "Rezepte",
|
||||
"themes": "Themen",
|
||||
"confirm": "Besstätigen"
|
||||
},
|
||||
"login": {
|
||||
"stay-logged-in": "Eingeloggt bleiben?",
|
||||
"email": "Email",
|
||||
"password": "Passwort",
|
||||
"sign-in": "Einloggen",
|
||||
"sign-up": "Registrieren"
|
||||
},
|
||||
"meal-plan": {
|
||||
"dinner-this-week": "Essen diese Woche",
|
||||
"dinner-today": "Heutiges Essen",
|
||||
"planner": "Planer",
|
||||
"edit-meal-plan": "Essensplan bearbeiten",
|
||||
"meal-plans": "Essenspläne",
|
||||
"create-a-new-meal-plan": "Neuen Essensplan erstellen",
|
||||
"start-date": "Start-Datum",
|
||||
"end-date": "End-Datum"
|
||||
},
|
||||
"recipe": {
|
||||
"description": "Beschreibung",
|
||||
"ingredients": "Zutaten",
|
||||
"categories": "Kategorien",
|
||||
"tags": "Markierungen",
|
||||
"instructions": "Anweisungen",
|
||||
"step-index": "Schritt: {step}",
|
||||
"recipe-name": "Rezeptname",
|
||||
"servings": "Portionen",
|
||||
"ingredient": "Zutat",
|
||||
"notes": "Notizen",
|
||||
"note": "Notiz",
|
||||
"original-url": "Original URL",
|
||||
"view-recipe": "Rezept anschauen",
|
||||
"title": "Titel",
|
||||
"total-time": "Gesamtzeit",
|
||||
"prep-time": "Vorbereitungszeit",
|
||||
"perform-time": "Kochzeit",
|
||||
"api-extras": "API Extras",
|
||||
"object-key": "Objektschlüssel",
|
||||
"object-value": "Objektwert",
|
||||
"new-key-name": "Neuer Schlüsselname",
|
||||
"add-key": "Schlüssel hinzufügen",
|
||||
"key-name-required": "Schlüsselname benötigt",
|
||||
"no-white-space-allowed": "Keine Leerschritte erlaubt",
|
||||
"delete-recipe": "Rezept löschen",
|
||||
"delete-confirmation": "Bist du sicher das du dieses Rezept löschen möchtest?"
|
||||
},
|
||||
"search": {
|
||||
"search-mealie": "Suche Mealie"
|
||||
},
|
||||
"settings": {
|
||||
"general-settings": "Einstellungen",
|
||||
"local-api": "Lokale API",
|
||||
"language": "Sprache",
|
||||
"add-a-new-theme": "Neues Thema hinzufügen",
|
||||
"set-new-time": "Neue Zeit einstellen",
|
||||
"current": "Version:",
|
||||
"latest": "Neuste",
|
||||
"explore-the-docs": "Stöbern",
|
||||
"contribute": "Beitragen",
|
||||
"backup-and-exports": "Sicherungen",
|
||||
"backup-info": "Sicherungen werden im standard JSON Format in das Dateisystem exportiert mitsamt sämtlicher Bilder. In deinem Sicherungsorder findest du eine ZIP Datei welche sämtliche JSON's deiner Rezepte und die Bilder aus der Datenbank enthält. Solltest du eine Markdown Datei auswählen werden diese ebenfalls im ZIP gespeichert. Um eine Sicherung zurückzuspielen muss die entsprechende ZIP Datei im Sicherungsorder liegen. Automatische Sicherungen finden jeden Tag um 3 Uhr früh statt.",
|
||||
"available-backups": "Verfügbare Sicherungen",
|
||||
"theme": {
|
||||
"theme-name": "Themenname",
|
||||
"theme-settings": "Themeneinstellungen",
|
||||
"select-a-theme-from-the-dropdown-or-create-a-new-theme-note-that-the-default-theme-will-be-served-to-all-users-who-have-not-set-a-theme-preference": "Wähle ein Thema aus der Dropdown-Liste oder erstelle ein neues. Beachte das das Standard Thema auf alle Benutzer angewandt wird die keine Einstellung für ein Thema getroffen haben.",
|
||||
"dark-mode": "Dunkler Modus",
|
||||
"theme-is-required": "Thema wird benötigt",
|
||||
"primary": "primär",
|
||||
"secondary": "sekundär",
|
||||
"accent": "betonen",
|
||||
"success": "Erfolg",
|
||||
"info": "Information",
|
||||
"warning": "Warnung",
|
||||
"error": "Fehler",
|
||||
"default-to-system": "Standardeinstellung",
|
||||
"light": "Hell",
|
||||
"dark": "Dunkel",
|
||||
"theme": "Thema",
|
||||
"saved-color-theme": "Buntes Thema gespeichert",
|
||||
"delete-theme": "Thema löschen",
|
||||
"are-you-sure-you-want-to-delete-this-theme": "Bist du sicher das du dieses Thema löschen möchtest?",
|
||||
"choose-how-mealie-looks-to-you-set-your-theme-preference-to-follow-your-system-settings-or-choose-to-use-the-light-or-dark-theme": "Entscheide wie Mealie für dich aussehen soll. Wähle Systemthema oder ob es Hell oder Dunkel dargestellt werden soll",
|
||||
"theme-name-is-required": "Theme Name is required."
|
||||
},
|
||||
"webhooks": {
|
||||
"meal-planner-webhooks": "Meal Planner Webhooks",
|
||||
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "Die unten stehenden URL's erhalten webhooks die die Rezeptdaten enthalten für den Menüplan am geplanten Tag. Derzeit werden die webhooks ausgeführt um",
|
||||
"test-webhooks": "Teste Webhooks",
|
||||
"webhook-url": "Webhook URL"
|
||||
},
|
||||
"new-version-available": "Eine neue Version von Mealie steht zur verfügung, <a {aContents}> Schau ins Repository </a>",
|
||||
"backup": {
|
||||
"import-recipes": "Rezepte importieren",
|
||||
"import-themes": "Themen importieren",
|
||||
"import-settings": "Einstellungen importieren",
|
||||
"create-heading": "Sicherung erstellen",
|
||||
"backup-tag": "Sicherungsmarkierung",
|
||||
"full-backup": "Komplettsicherungen",
|
||||
"partial-backup": "Teilsicherungen",
|
||||
"backup-restore-report": "Sicherungs/Widerherstellungsbericht",
|
||||
"successfully-imported": "Erfolgreich importiert",
|
||||
"failed-imports": "Import fehlgeschlagen"
|
||||
}
|
||||
},
|
||||
"migration": {
|
||||
"recipe-migration": "Rezepte übertragen",
|
||||
"failed-imports": "Fehlgeschlagene Importe",
|
||||
"migration-report": "Übertragungsbericht",
|
||||
"successful-imports": "Erfolgreiche Importe",
|
||||
"no-migration-data-available": "Keine Übertragungsdaten verfügbar",
|
||||
"nextcloud": {
|
||||
"title": "Nextcloud Cookbook",
|
||||
"description": "Übertrage Daten aus einer Nextcloud Cookbook Instanz"
|
||||
},
|
||||
"chowdown": {
|
||||
"title": "Chowdown",
|
||||
"description": "Übertrage Daten aus Chowdown"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,9 @@
|
||||
"sign-up": "Sign up"
|
||||
},
|
||||
"meal-plan": {
|
||||
"shopping-list": "Shopping List",
|
||||
"dinner-this-week": "Dinner This Week",
|
||||
"meal-planner": "Meal Planner",
|
||||
"dinner-today": "Dinner Today",
|
||||
"planner": "Planner",
|
||||
"edit-meal-plan": "Edit Meal Plan",
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Copiez votre recette ici. Chaque ligne sera traitée comme un objet de la liste."
|
||||
},
|
||||
"general": {
|
||||
"upload": "Téléverser",
|
||||
"submit": "Soumettre",
|
||||
"name": "Nom",
|
||||
"settings": "Options",
|
||||
@@ -28,7 +29,12 @@
|
||||
"ok": "OK",
|
||||
"enabled": "Activé",
|
||||
"download": "Télécharger",
|
||||
"import": "Importer"
|
||||
"import": "Importer",
|
||||
"options": "Options",
|
||||
"templates": "Modèles",
|
||||
"recipes": "Recettes",
|
||||
"themes": "Thèmes",
|
||||
"confirm": "Confirmer"
|
||||
},
|
||||
"login": {
|
||||
"stay-logged-in": "Rester connecté(e) ?",
|
||||
@@ -60,21 +66,39 @@
|
||||
"notes": "Notes",
|
||||
"note": "Note",
|
||||
"original-url": "Recette originale",
|
||||
"view-recipe": "Voir la recette"
|
||||
"view-recipe": "Voir la recette",
|
||||
"title": "Titre",
|
||||
"total-time": "Temps total",
|
||||
"prep-time": "Temps de préparation",
|
||||
"perform-time": "Temps de cuisson",
|
||||
"api-extras": "Extras API",
|
||||
"object-key": "Clé d'objet",
|
||||
"object-value": "Valeur d'objet",
|
||||
"new-key-name": "Nouveau nom de clé",
|
||||
"add-key": "Ajouter une clé",
|
||||
"key-name-required": "Un nom de clé est requis",
|
||||
"no-white-space-allowed": "Aucun espace blanc autorisé",
|
||||
"delete-recipe": "Supprimer la recette",
|
||||
"delete-confirmation": "Êtes-vous sûr(e) de vouloir supprimer cette recette ?"
|
||||
},
|
||||
"search": {
|
||||
"search-mealie": "Search Mealie"
|
||||
"search-mealie": "Rechercher dans Mealie"
|
||||
},
|
||||
"settings": {
|
||||
"general-settings": "Paramètres généraux",
|
||||
"local-api": "API local",
|
||||
"language": "Langue",
|
||||
"add-a-new-theme": "Ajouter un nouveau thème",
|
||||
"set-new-time": "Définir une nouvelle heure d'exécution",
|
||||
"set-new-time": "Indiquer un nouveau temps",
|
||||
"current": "Version :",
|
||||
"latest": "Dernière",
|
||||
"explore-the-docs": "Parcourir la documentation",
|
||||
"contribute": "Contribuer",
|
||||
"backup-and-exports": "Sauver et exporter",
|
||||
"backup-info": "Les sauvegardes sont exportées en format JSON standard, ainsi que toutes les images stockées sur le système. Dans votre dossier de sauvegarde, vous trouverez un dossier .zip qui contient toutes les recettes en JSON et les images de la base de données. De plus, si vous avez sélectionné le format de fichier markdown, il sera sauvegardé dans le même dossier .zip. Pour importer une sauvegarde, celle-ci doit être enregistrée dans votre dossier de sauvegardes. Une sauvegarde automatique est effectuée quotidiennement à 03h00.",
|
||||
"available-backups": "Sauvegardes disponibles",
|
||||
"theme": {
|
||||
"theme-name": "Nom du thème",
|
||||
"theme-settings": "Paramètres du thème",
|
||||
"select-a-theme-from-the-dropdown-or-create-a-new-theme-note-that-the-default-theme-will-be-served-to-all-users-who-have-not-set-a-theme-preference": "Sélectionnez un thème depuis la liste ou créez-en un nouveau. Le thème par défaut sera utilisé pour tous les utilisateurs qui n'ont pas choisi de thème personnalisé.",
|
||||
"dark-mode": "Mode sombre",
|
||||
@@ -86,13 +110,15 @@
|
||||
"info": "Information",
|
||||
"warning": "Avertissement",
|
||||
"error": "Erreur",
|
||||
"default-to-system": "Identique au système",
|
||||
"light": "Clair",
|
||||
"dark": "Sombre",
|
||||
"theme": "Thème",
|
||||
"saved-color-theme": "Thèmes sauvegardés",
|
||||
"delete-theme": "Supprimer le thème",
|
||||
"are-you-sure-you-want-to-delete-this-theme": "Etes-vous sûr(e) de vouloir supprimer ce thème ?",
|
||||
"choose-how-mealie-looks-to-you-set-your-theme-preference-to-follow-your-system-settings-or-choose-to-use-the-light-or-dark-theme": "Personnalisez l'apparence de Mealie. Utilisez le thème par défaut de votre système ou choisissez manuellement entre le thème clair ou sombre."
|
||||
"choose-how-mealie-looks-to-you-set-your-theme-preference-to-follow-your-system-settings-or-choose-to-use-the-light-or-dark-theme": "Personnalisez l'apparence de Mealie. Utilisez le thème par défaut de votre système ou choisissez manuellement entre le thème clair ou sombre.",
|
||||
"theme-name-is-required": "Un nom de thème est requis."
|
||||
},
|
||||
"webhooks": {
|
||||
"meal-planner-webhooks": "Webhooks du planificateur de repas",
|
||||
@@ -104,11 +130,29 @@
|
||||
"backup": {
|
||||
"import-recipes": "Importer des recettes",
|
||||
"import-themes": "Importer des thèmes",
|
||||
"import-settings": "Importer des paramètres"
|
||||
"import-settings": "Importer des paramètres",
|
||||
"create-heading": "Créer une sauvegarde",
|
||||
"backup-tag": "Tag de la sauvegarde",
|
||||
"full-backup": "Sauvegarde complète",
|
||||
"partial-backup": "Sauvegarde partielle",
|
||||
"backup-restore-report": "Rapport de la restauration de la sauvegarde",
|
||||
"successfully-imported": "Importé avec succès",
|
||||
"failed-imports": "Importations échouées"
|
||||
}
|
||||
},
|
||||
"migration": {
|
||||
"recipe-migration": "Migrer les recettes",
|
||||
"failed-imports": "Importations échouées"
|
||||
"failed-imports": "Importations échouées",
|
||||
"migration-report": "Rapport de migration",
|
||||
"successful-imports": "Importations réussies",
|
||||
"no-migration-data-available": "Aucune donnée d'importation n'est disponible",
|
||||
"nextcloud": {
|
||||
"title": "Livre de recettes Nextcloud (Cookbook)",
|
||||
"description": "Importer des recettes depuis un livre de recettes Nextcloud existant"
|
||||
},
|
||||
"chowdown": {
|
||||
"title": "Chowdown",
|
||||
"description": "Importer des recettes depuis Chowdown"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
158
frontend/src/locales/pl.json
Normal file
158
frontend/src/locales/pl.json
Normal file
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"404": {
|
||||
"page-not-found": "404 Strony nie odnaleziono",
|
||||
"take-me-home": "Powrót na stronę główną"
|
||||
},
|
||||
"new-recipe": {
|
||||
"from-url": "Z odnośnika",
|
||||
"recipe-url": "Odnośnik przepisu",
|
||||
"error-message": "Wygląda na to, że wystąpił błąd. Sprawdź log i debug/last_recipe.json aby zasięgnąć po więcej informacji.",
|
||||
"bulk-add": "Dodanie zbiorcze",
|
||||
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Przeklej zawartość przepisu. Każda indywidualna linia traktowana będzie jako pozycja na liście"
|
||||
},
|
||||
"general": {
|
||||
"upload": "Wrzuć",
|
||||
"submit": "Zatwierdź",
|
||||
"name": "Nazwa",
|
||||
"settings": "Ustawienia",
|
||||
"close": "Zamknij",
|
||||
"save": "Zapisz",
|
||||
"image-file": "Plik obrazu",
|
||||
"update": "Uaktualnij",
|
||||
"edit": "Edytuj",
|
||||
"delete": "Usuń",
|
||||
"select": "Zaznacz",
|
||||
"random": "Losowa",
|
||||
"new": "Nowa",
|
||||
"create": "Utwórz",
|
||||
"cancel": "Anuluj",
|
||||
"ok": "OK",
|
||||
"enabled": "Włączone",
|
||||
"download": "Pobierz",
|
||||
"import": "Importuj",
|
||||
"options": "Opcje",
|
||||
"templates": "Szablony",
|
||||
"recipes": "Przepisy",
|
||||
"themes": "Motywy",
|
||||
"confirm": "Potwierdź"
|
||||
},
|
||||
"login": {
|
||||
"stay-logged-in": "Pozostań zalogowany",
|
||||
"email": "Email",
|
||||
"password": "Hasło",
|
||||
"sign-in": "Zaloguj się",
|
||||
"sign-up": "Zarejestruj się"
|
||||
},
|
||||
"meal-plan": {
|
||||
"dinner-this-week": "Obiad w tym tygodniu",
|
||||
"dinner-today": "Obiad dziś",
|
||||
"planner": "Planer",
|
||||
"edit-meal-plan": "Edytuj plan posiłku",
|
||||
"meal-plans": "Plany posiłku",
|
||||
"create-a-new-meal-plan": "Utwórz nowy plan posiłku",
|
||||
"start-date": "Data rozpoczęcia",
|
||||
"end-date": "Data zakończenia"
|
||||
},
|
||||
"recipe": {
|
||||
"description": "Opis",
|
||||
"ingredients": "Składniki",
|
||||
"categories": "Kategorie",
|
||||
"tags": "Etykiety",
|
||||
"instructions": "Instrukcje",
|
||||
"step-index": "Krok: {step}",
|
||||
"recipe-name": "Nazwa przepisu",
|
||||
"servings": "Porcje",
|
||||
"ingredient": "Składnik",
|
||||
"notes": "Notatki",
|
||||
"note": "Notatka",
|
||||
"original-url": "Oryginalny odnośnik",
|
||||
"view-recipe": "Wyświetl przepis",
|
||||
"title": "Tytuł",
|
||||
"total-time": "Czas całkowity",
|
||||
"prep-time": "Czas przyrządzania",
|
||||
"perform-time": "Czas gotowania",
|
||||
"api-extras": "Dodatki API",
|
||||
"object-key": "Klucz obiektu",
|
||||
"object-value": "Wartość obiektu",
|
||||
"new-key-name": "Nazwa nowego klucza",
|
||||
"add-key": "Dodaj klucz",
|
||||
"key-name-required": "Nazwa klucza jest wymagana",
|
||||
"no-white-space-allowed": "Znaki niedrukowalne są niedozwolone",
|
||||
"delete-recipe": "Usuń przepis",
|
||||
"delete-confirmation": "Czy jesteś pewien, że chcesz usunąć ten przepis?"
|
||||
},
|
||||
"search": {
|
||||
"search-mealie": "Przeszukaj Mealie"
|
||||
},
|
||||
"settings": {
|
||||
"general-settings": "Ustawienia główne",
|
||||
"local-api": "Lokalne API",
|
||||
"language": "Język",
|
||||
"add-a-new-theme": "Dodaj nowy motyw",
|
||||
"set-new-time": "Ustaw nowy czas",
|
||||
"current": "Wersja:",
|
||||
"latest": "Najnowsza",
|
||||
"explore-the-docs": "Zobacz dokumentację",
|
||||
"contribute": "Wspomóż",
|
||||
"backup-and-exports": "Kopie zapasowe",
|
||||
"backup-info": "Kopie zapasowe zapisywane są w standardowym formacie JSON wraz ze zdjęciami w systemie plików. W katalogu kopii zapasowej znajdziesz plik z rozszerzeniem .zip zawierający wszystkie przepisy i zdjęcia z bazy danych. Jeśli oznaczone zostały pliki markdown, one także znajdą się w pliku .zip. Aby zaimportować kopię, musi ona znajdować się w folderze kopii zapasowych. Kopie automatyczne tworzone są codziennie o godzinie 03:00.",
|
||||
"available-backups": "Dostępne kopie zapsowe",
|
||||
"theme": {
|
||||
"theme-name": "Nazwa motywu",
|
||||
"theme-settings": "Ustawienia motywu",
|
||||
"select-a-theme-from-the-dropdown-or-create-a-new-theme-note-that-the-default-theme-will-be-served-to-all-users-who-have-not-set-a-theme-preference": "Wybierz motyw z rozwijanej listy bądź stwórz nowy. Domyślny motyw zostanie użyty dla wszystkich użytkowników którzy nie wybrali własnej preferencji.",
|
||||
"dark-mode": "Ciemny motyw",
|
||||
"theme-is-required": "Motyw jest wymagany",
|
||||
"primary": "Pierwszorzędny",
|
||||
"secondary": "Drugorzędny",
|
||||
"accent": "Akcent",
|
||||
"success": "Powodzenie",
|
||||
"info": "Informacja",
|
||||
"warning": "Ostrzeżenie",
|
||||
"error": "Błąd",
|
||||
"default-to-system": "Domyślny dla systemu",
|
||||
"light": "Jasny",
|
||||
"dark": "Ciemny",
|
||||
"theme": "Motyw",
|
||||
"saved-color-theme": "Zapisany kolor motywu",
|
||||
"delete-theme": "Usuń motyw",
|
||||
"are-you-sure-you-want-to-delete-this-theme": "Czy jesteś pewien, że chcesz usunąć ten motyw?",
|
||||
"choose-how-mealie-looks-to-you-set-your-theme-preference-to-follow-your-system-settings-or-choose-to-use-the-light-or-dark-theme": "Wybierz jak Mealie ma dla Ciebie wyglądać. Dostępne opcje to podążanie za odcieniem systemowym, bądź motyw jasny lub ciemny.",
|
||||
"theme-name-is-required": "Nazwa motywu jest wymagana."
|
||||
},
|
||||
"webhooks": {
|
||||
"meal-planner-webhooks": "Webhooki planera posiłków",
|
||||
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "Odnośniki poniżej otrzymają webhook zawierający dane o przepisie dla danego dnia. Aktualnie webhooki zostanę wykonane o",
|
||||
"test-webhooks": "Testuj webhooki",
|
||||
"webhook-url": "Odnośnik webhooka"
|
||||
},
|
||||
"new-version-available": "Dostępna jest nowa wersja Mealie, <a {aContents}> sprawdź repozytorium </a>",
|
||||
"backup": {
|
||||
"import-recipes": "Wgraj przepisy",
|
||||
"import-themes": "Wgraj motywy",
|
||||
"import-settings": "Wgraj ustawienia",
|
||||
"create-heading": "Utwórz kopię zapasową",
|
||||
"backup-tag": "Etykieta kopii zapasowej",
|
||||
"full-backup": "Pełna kopia zapasowa",
|
||||
"partial-backup": "Częściowa kopia zapasowa",
|
||||
"backup-restore-report": "Raport przywrócenia kopii zapasowej",
|
||||
"successfully-imported": "Import zakończony suckesem",
|
||||
"failed-imports": "Importy nieudane"
|
||||
}
|
||||
},
|
||||
"migration": {
|
||||
"recipe-migration": "Przenoszenie przepisów",
|
||||
"failed-imports": "Importy udane",
|
||||
"migration-report": "Raport przenosin",
|
||||
"successful-imports": "Importy nieudane",
|
||||
"no-migration-data-available": "Brak danych do przeniesienia",
|
||||
"nextcloud": {
|
||||
"title": "Nextcloud Cookbook",
|
||||
"description": "Przenieś dane z Nextcloud Cookbook"
|
||||
},
|
||||
"chowdown": {
|
||||
"title": "Chowdown",
|
||||
"description": "Przenieś dane z Chowdown"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,9 @@ import store from "./store/store";
|
||||
import VueRouter from "vue-router";
|
||||
import { routes } from "./routes";
|
||||
import i18n from "./i18n";
|
||||
import FlashMessage from "@smartweb/vue-flash-message";
|
||||
|
||||
Vue.use(FlashMessage);
|
||||
Vue.config.productionTip = false;
|
||||
Vue.use(VueRouter);
|
||||
|
||||
@@ -14,16 +16,16 @@ const router = new VueRouter({
|
||||
mode: process.env.NODE_ENV === "production" ? "history" : "hash",
|
||||
});
|
||||
|
||||
new Vue({
|
||||
const vueApp = new Vue({
|
||||
vuetify,
|
||||
store,
|
||||
router,
|
||||
i18n,
|
||||
render: (h) => h(App),
|
||||
render: h => h(App),
|
||||
}).$mount("#app");
|
||||
|
||||
// Truncate
|
||||
let truncate = function (text, length, clamp) {
|
||||
let truncate = function(text, length, clamp) {
|
||||
clamp = clamp || "...";
|
||||
let node = document.createElement("div");
|
||||
node.innerHTML = text;
|
||||
@@ -31,11 +33,12 @@ let truncate = function (text, length, clamp) {
|
||||
return content.length > length ? content.slice(0, length) + clamp : content;
|
||||
};
|
||||
|
||||
let titleCase = function (value) {
|
||||
return value.replace(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
|
||||
let titleCase = function(value) {
|
||||
return value.replace(/(?:^|\s|-)\S/g, x => x.toUpperCase());
|
||||
};
|
||||
|
||||
Vue.filter("truncate", truncate);
|
||||
Vue.filter("titleCase", titleCase);
|
||||
|
||||
export { vueApp };
|
||||
export { router };
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../api";
|
||||
import api from "@/api";
|
||||
import CardSection from "../components/UI/CardSection";
|
||||
import CategorySidebar from "../components/UI/CategorySidebar";
|
||||
export default {
|
||||
|
||||
20
frontend/src/pages/Debug.vue
Normal file
20
frontend/src/pages/Debug.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div>
|
||||
<LastRecipe />
|
||||
<LogFile class="mt-2" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LastRecipe from "@/components/Debug/LastRecipe";
|
||||
import LogFile from "@/components/Debug/LogFile";
|
||||
export default {
|
||||
components: {
|
||||
LastRecipe,
|
||||
LogFile,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -21,7 +21,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../api";
|
||||
import api from "@/api";
|
||||
import CardSection from "../components/UI/CardSection";
|
||||
import CategorySidebar from "../components/UI/CategorySidebar";
|
||||
export default {
|
||||
@@ -55,7 +55,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async buildPage() {
|
||||
this.homeCategories.forEach(async (element) => {
|
||||
this.homeCategories.forEach(async element => {
|
||||
let recipes = await this.getRecipeByCategory(element.slug);
|
||||
recipes.position = element.position;
|
||||
this.recipeByCategory.push(recipes);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
@updated="planUpdated"
|
||||
/>
|
||||
<NewMeal v-else @created="requestMeals" class="mb-5" />
|
||||
<ShoppingListDialog ref="shoppingList" />
|
||||
|
||||
<v-card class="my-2">
|
||||
<v-card-title class="headline">
|
||||
@@ -49,6 +50,14 @@
|
||||
</v-list-item-group>
|
||||
</v-list>
|
||||
<v-card-actions class="mt-n5">
|
||||
<v-btn
|
||||
color="accent lighten-2"
|
||||
class="mx-0"
|
||||
text
|
||||
@click="openShoppingList(mealplan.uid)"
|
||||
>
|
||||
{{ $t("meal-plan.shopping-list") }}
|
||||
</v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
color="accent lighten-2"
|
||||
@@ -74,15 +83,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../api";
|
||||
import utils from "../utils";
|
||||
import api from "@/api";
|
||||
import utils from "@/utils";
|
||||
import NewMeal from "../components/MealPlan/MealPlanNew";
|
||||
import EditPlan from "../components/MealPlan/MealPlanEditor";
|
||||
import ShoppingListDialog from "../components/MealPlan/ShoppingListDialog";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NewMeal,
|
||||
EditPlan,
|
||||
ShoppingListDialog,
|
||||
},
|
||||
data: () => ({
|
||||
plannedMeals: [],
|
||||
@@ -122,6 +133,9 @@ export default {
|
||||
api.mealPlans.delete(id);
|
||||
this.requestMeals();
|
||||
},
|
||||
openShoppingList(id) {
|
||||
this.$refs.shoppingList.openDialog(id);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../api";
|
||||
import utils from "../utils";
|
||||
import api from "@/api";
|
||||
import utils from "@/utils";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../api";
|
||||
import api from "@/api";
|
||||
|
||||
import RecipeEditor from "../components/Recipe/RecipeEditor";
|
||||
import VJsoneditor from "v-jsoneditor";
|
||||
|
||||
@@ -56,8 +56,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "../api";
|
||||
import utils from "../utils";
|
||||
import api from "@/api";
|
||||
import utils from "@/utils";
|
||||
import VJsoneditor from "v-jsoneditor";
|
||||
import RecipeViewer from "../components/Recipe/RecipeViewer";
|
||||
import RecipeEditor from "../components/Recipe/RecipeEditor";
|
||||
@@ -107,7 +107,7 @@ export default {
|
||||
},
|
||||
|
||||
watch: {
|
||||
$route: function () {
|
||||
$route: function() {
|
||||
this.getRecipeDetails();
|
||||
},
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<General />
|
||||
<Theme class="mt-2" />
|
||||
<Backup class="mt-2" />
|
||||
<Webhooks class="mt-2" />
|
||||
<MealPlanner class="mt-2" />
|
||||
<Migration class="mt-2" />
|
||||
<p class="text-center my-2">
|
||||
{{ $t("settings.current") }}
|
||||
@@ -41,15 +41,16 @@
|
||||
<script>
|
||||
import Backup from "../components/Settings/Backup";
|
||||
import General from "../components/Settings/General";
|
||||
import Webhooks from "../components/Settings/Webhook";
|
||||
import MealPlanner from "../components/Settings/MealPlanner";
|
||||
import Theme from "../components/Settings/Theme";
|
||||
import Migration from "../components/Settings/Migration";
|
||||
import api from "@/api";
|
||||
import axios from "axios";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Backup,
|
||||
Webhooks,
|
||||
MealPlanner,
|
||||
Theme,
|
||||
Migration,
|
||||
General,
|
||||
@@ -57,11 +58,13 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
latestVersion: null,
|
||||
version: "v0.1.0",
|
||||
version: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
async mounted() {
|
||||
this.getVersion();
|
||||
let versionData = await api.meta.get_version();
|
||||
this.version = versionData.version;
|
||||
},
|
||||
computed: {
|
||||
newVersion() {
|
||||
|
||||
@@ -6,6 +6,8 @@ Vue.use(Vuetify);
|
||||
const vuetify = new Vuetify({
|
||||
theme: {
|
||||
dark: false,
|
||||
options: { customProperties: true },
|
||||
|
||||
themes: {
|
||||
light: {
|
||||
primary: "#E58325",
|
||||
|
||||
@@ -7,12 +7,14 @@ import SettingsPage from "./pages/SettingsPage";
|
||||
import AllRecipesPage from "./pages/AllRecipesPage";
|
||||
import CategoryPage from "./pages/CategoryPage";
|
||||
import MeaplPlanPage from "./pages/MealPlanPage";
|
||||
import Debug from "./pages/Debug";
|
||||
import MealPlanThisWeekPage from "./pages/MealPlanThisWeekPage";
|
||||
import api from "./api";
|
||||
import api from "@/api";
|
||||
|
||||
export const routes = [
|
||||
{ path: "/", component: HomePage },
|
||||
{ path: "/mealie", component: HomePage },
|
||||
{ path: "/debug", component: Debug },
|
||||
{ path: "/search", component: SearchPage },
|
||||
{ path: "/recipes/all", component: AllRecipesPage },
|
||||
{ path: "/recipes/:category", component: CategoryPage },
|
||||
@@ -24,7 +26,7 @@ export const routes = [
|
||||
{
|
||||
path: "/meal-plan/today",
|
||||
beforeEnter: async (_to, _from, next) => {
|
||||
await todaysMealRoute().then((redirect) => {
|
||||
await todaysMealRoute().then(redirect => {
|
||||
next(redirect);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import api from "../../api";
|
||||
import api from "@/api";
|
||||
|
||||
const state = {
|
||||
showRecent: true,
|
||||
@@ -15,7 +15,7 @@ const mutations = {
|
||||
state.showLimit = payload;
|
||||
},
|
||||
setCategories(state, payload) {
|
||||
state.categories = payload;
|
||||
state.categories = payload.sort((a, b) => (a.name > b.name ? 1 : -1));
|
||||
},
|
||||
setHomeCategories(state, payload) {
|
||||
state.homeCategories = payload;
|
||||
@@ -30,10 +30,10 @@ const actions = {
|
||||
};
|
||||
|
||||
const getters = {
|
||||
getShowRecent: (state) => state.showRecent,
|
||||
getShowLimit: (state) => state.showLimit,
|
||||
getCategories: (state) => state.categories,
|
||||
getHomeCategories: (state) => state.homeCategories,
|
||||
getShowRecent: state => state.showRecent,
|
||||
getShowLimit: state => state.showLimit,
|
||||
getCategories: state => state.categories,
|
||||
getHomeCategories: state => state.homeCategories,
|
||||
};
|
||||
|
||||
export default {
|
||||
|
||||
@@ -15,6 +15,10 @@ const state = {
|
||||
name: "French",
|
||||
value: "fr",
|
||||
},
|
||||
{
|
||||
name: "Polish",
|
||||
value: "pl",
|
||||
},
|
||||
{
|
||||
name: "Swedish",
|
||||
value: "sv",
|
||||
@@ -27,6 +31,10 @@ const state = {
|
||||
name: "繁體中文",
|
||||
value: "zh-TW",
|
||||
},
|
||||
{
|
||||
name: "German",
|
||||
value: "de",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import api from "../../api";
|
||||
import api from "@/api";
|
||||
import Vuetify from "../../plugins/vuetify";
|
||||
|
||||
function inDarkMode(payload) {
|
||||
@@ -60,9 +60,9 @@ const actions = {
|
||||
};
|
||||
|
||||
const getters = {
|
||||
getActiveTheme: (state) => state.activeTheme,
|
||||
getDarkMode: (state) => state.darkMode,
|
||||
getIsDark: (state) => state.isDark,
|
||||
getActiveTheme: state => state.activeTheme,
|
||||
getDarkMode: state => state.darkMode,
|
||||
getIsDark: state => state.isDark,
|
||||
};
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Vue from "vue";
|
||||
import Vuex from "vuex";
|
||||
import api from "../api";
|
||||
import api from "@/api";
|
||||
import createPersistedState from "vuex-persistedstate";
|
||||
import userSettings from "./modules/userSettings";
|
||||
import language from "./modules/language";
|
||||
@@ -29,6 +29,7 @@ const store = new Vuex.Store({
|
||||
// All Recipe Data Store
|
||||
recentRecipes: [],
|
||||
allRecipes: [],
|
||||
mealPlanCategories: [],
|
||||
},
|
||||
|
||||
mutations: {
|
||||
@@ -44,6 +45,10 @@ const store = new Vuex.Store({
|
||||
setRecentRecipes(state, payload) {
|
||||
state.recentRecipes = payload;
|
||||
},
|
||||
|
||||
setMealPlanCategories(state, payload) {
|
||||
state.mealPlanCategories = payload;
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
@@ -64,11 +69,12 @@ const store = new Vuex.Store({
|
||||
|
||||
getters: {
|
||||
//
|
||||
getSnackText: (state) => state.snackText,
|
||||
getSnackActive: (state) => state.snackActive,
|
||||
getSnackType: (state) => state.snackType,
|
||||
getSnackText: state => state.snackText,
|
||||
getSnackActive: state => state.snackActive,
|
||||
getSnackType: state => state.snackType,
|
||||
|
||||
getRecentRecipes: (state) => state.recentRecipes,
|
||||
getRecentRecipes: state => state.recentRecipes,
|
||||
getMealPlanCategories: state => state.mealPlanCategories,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
// import utils from "../../utils";
|
||||
// import utils from "@/utils";
|
||||
// import Vue from "vue";
|
||||
// import Vuetify from "./plugins/vuetify";
|
||||
import { vueApp } from "./main";
|
||||
|
||||
const notifyHelpers = {
|
||||
baseCSS: "notify-base",
|
||||
error: "notify-error-color",
|
||||
warning: "notify-warning-color",
|
||||
success: "notify-success-color",
|
||||
info: "notify-info-color",
|
||||
};
|
||||
|
||||
const days = [
|
||||
"Sunday",
|
||||
@@ -72,4 +81,28 @@ export default {
|
||||
|
||||
return `${year}-${month}-${day}`;
|
||||
},
|
||||
notify: {
|
||||
show: function(text, type = "info", title = null) {
|
||||
vueApp.flashMessage.show({
|
||||
status: type,
|
||||
title: title,
|
||||
message: text,
|
||||
time: 3000,
|
||||
blockClass: `${notifyHelpers.baseCSS} ${notifyHelpers[type]}`,
|
||||
contentClass: `${notifyHelpers.baseCSS} ${notifyHelpers[type]}`,
|
||||
});
|
||||
},
|
||||
info: function(text, title = null) {
|
||||
this.show(text, "info", title);
|
||||
},
|
||||
success: function(text, title = null) {
|
||||
this.show(text, "success", title);
|
||||
},
|
||||
error: function(text, title = null) {
|
||||
this.show(text, "error", title);
|
||||
},
|
||||
warning: function(text, title = null) {
|
||||
this.show(text, "warning", title);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module.exports = {
|
||||
transpileDependencies: ["vuetify"],
|
||||
publicPath: process.env.NODE_ENV === "production" ? "/static/" : "/",
|
||||
publicPath: process.env.NODE_ENV === "production" ? "/" : "/",
|
||||
outputDir: process.env.NODE_ENV === "production" ? "./dist" : "../mealie/web",
|
||||
devServer: {
|
||||
proxy: {
|
||||
@@ -12,10 +12,10 @@ module.exports = {
|
||||
},
|
||||
pluginOptions: {
|
||||
i18n: {
|
||||
locale: 'en',
|
||||
fallbackLocale: 'en',
|
||||
localeDir: 'locales',
|
||||
enableInSFC: true
|
||||
}
|
||||
}
|
||||
locale: "en",
|
||||
fallbackLocale: "en",
|
||||
localeDir: "locales",
|
||||
enableInSFC: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import uvicorn
|
||||
from fastapi import FastAPI
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
# import utils.startup as startup
|
||||
from app_config import PORT, PRODUCTION, WEB_PATH, docs_url, redoc_url
|
||||
from app_config import APP_VERSION, PORT, PRODUCTION, docs_url, redoc_url
|
||||
from routes import (
|
||||
backup_routes,
|
||||
debug_routes,
|
||||
meal_routes,
|
||||
migration_routes,
|
||||
setting_routes,
|
||||
static_routes,
|
||||
theme_routes,
|
||||
user_routes,
|
||||
)
|
||||
from routes.recipe import (
|
||||
all_recipe_routes,
|
||||
@@ -20,38 +17,27 @@ from routes.recipe import (
|
||||
recipe_crud_routes,
|
||||
tag_routes,
|
||||
)
|
||||
from utils.api_docs import generate_api_docs
|
||||
from services.settings_services import default_settings_init
|
||||
from utils.logger import logger
|
||||
|
||||
"""
|
||||
TODO:
|
||||
- [x] Fix Duplicate Category
|
||||
- [x] Fix category overflow
|
||||
- [ ] Enable Database Name Versioning
|
||||
- [ ] Finish Frontend Category Management
|
||||
- [x] Delete Category
|
||||
- [ ] Sort Sidebar A-Z
|
||||
- [ ] Refactor Test Endpoints - Abstract to fixture?
|
||||
|
||||
|
||||
"""
|
||||
app = FastAPI(
|
||||
title="Mealie",
|
||||
description="A place for all your recipes",
|
||||
version="0.0.1",
|
||||
version=APP_VERSION,
|
||||
docs_url=docs_url,
|
||||
redoc_url=redoc_url,
|
||||
)
|
||||
|
||||
|
||||
def mount_static_files():
|
||||
app.mount("/static", StaticFiles(directory=WEB_PATH, html=True))
|
||||
|
||||
|
||||
def start_scheduler():
|
||||
import services.scheduler.scheduled_jobs
|
||||
|
||||
|
||||
def init_settings():
|
||||
default_settings_init()
|
||||
import services.theme_services
|
||||
|
||||
|
||||
def api_routers():
|
||||
# Recipes
|
||||
app.include_router(all_recipe_routes.router)
|
||||
@@ -65,32 +51,15 @@ def api_routers():
|
||||
app.include_router(theme_routes.router)
|
||||
# Backups/Imports Routes
|
||||
app.include_router(backup_routes.router)
|
||||
# User Routes
|
||||
app.include_router(user_routes.router)
|
||||
# Migration Routes
|
||||
app.include_router(migration_routes.router)
|
||||
app.include_router(debug_routes.router)
|
||||
|
||||
|
||||
if PRODUCTION:
|
||||
mount_static_files()
|
||||
|
||||
api_routers()
|
||||
|
||||
# API 404 Catch all CALL AFTER ROUTERS
|
||||
@app.get("/api/{full_path:path}", status_code=404, include_in_schema=False)
|
||||
def invalid_api():
|
||||
return None
|
||||
|
||||
|
||||
app.include_router(static_routes.router)
|
||||
|
||||
|
||||
# Generate API Documentation
|
||||
# if not PRODUCTION:
|
||||
# generate_api_docs(app)
|
||||
|
||||
start_scheduler()
|
||||
init_settings()
|
||||
|
||||
if __name__ == "__main__":
|
||||
logger.info("-----SYSTEM STARTUP-----")
|
||||
@@ -101,6 +70,7 @@ if __name__ == "__main__":
|
||||
port=PORT,
|
||||
reload=True,
|
||||
debug=True,
|
||||
log_level="info",
|
||||
workers=1,
|
||||
forwarded_allow_ips="*",
|
||||
)
|
||||
|
||||
@@ -15,14 +15,32 @@ def ensure_dirs():
|
||||
ENV = CWD.joinpath(".env")
|
||||
dotenv.load_dotenv(ENV)
|
||||
|
||||
# General
|
||||
APP_VERSION = "v0.3.0"
|
||||
DB_VERSION = "v0.2.1"
|
||||
PRODUCTION = os.environ.get("ENV")
|
||||
PORT = int(os.getenv("mealie_port", 9000))
|
||||
API = os.getenv("api_docs", True)
|
||||
|
||||
if API:
|
||||
docs_url = "/docs"
|
||||
redoc_url = "/redoc"
|
||||
else:
|
||||
docs_url = None
|
||||
redoc_url = None
|
||||
|
||||
# Helpful Globals
|
||||
BASE_DIR = CWD
|
||||
DATA_DIR = CWD.parent.joinpath("app_data")
|
||||
if PRODUCTION:
|
||||
DATA_DIR = Path("/app/data")
|
||||
|
||||
WEB_PATH = CWD.joinpath("dist")
|
||||
IMG_DIR = DATA_DIR.joinpath("img")
|
||||
BACKUP_DIR = DATA_DIR.joinpath("backups")
|
||||
DEBUG_DIR = DATA_DIR.joinpath("debug")
|
||||
MIGRATION_DIR = DATA_DIR.joinpath("migration")
|
||||
NEXTCLOUD_DIR = MIGRATION_DIR.joinpath("nextcloud")
|
||||
CHOWDOWN_DIR = MIGRATION_DIR.joinpath("chowdown")
|
||||
TEMPLATE_DIR = DATA_DIR.joinpath("templates")
|
||||
SQLITE_DIR = DATA_DIR.joinpath("db")
|
||||
TEMP_DIR = DATA_DIR.joinpath(".temp")
|
||||
@@ -35,31 +53,23 @@ REQUIRED_DIRS = [
|
||||
MIGRATION_DIR,
|
||||
TEMPLATE_DIR,
|
||||
SQLITE_DIR,
|
||||
NEXTCLOUD_DIR,
|
||||
CHOWDOWN_DIR,
|
||||
]
|
||||
|
||||
APP_VERSION = "v0.2.0"
|
||||
# General
|
||||
PRODUCTION = os.environ.get("ENV")
|
||||
PORT = int(os.getenv("mealie_port", 9000))
|
||||
API = os.getenv("api_docs", True)
|
||||
ensure_dirs()
|
||||
|
||||
if API:
|
||||
docs_url = "/docs"
|
||||
redoc_url = "/redoc"
|
||||
else:
|
||||
docs_url = None
|
||||
redoc_url = None
|
||||
|
||||
SQLITE_FILE = None
|
||||
# DATABASE ENV
|
||||
DATABASE_TYPE = os.getenv("db_type", "sqlite") # mongo, sqlite
|
||||
SQLITE_FILE = None
|
||||
DATABASE_TYPE = os.getenv("db_type", "sqlite")
|
||||
if DATABASE_TYPE == "sqlite":
|
||||
USE_SQL = True
|
||||
SQLITE_FILE = SQLITE_DIR.joinpath(f"mealie_{APP_VERSION}.sqlite")
|
||||
SQLITE_FILE = SQLITE_DIR.joinpath(f"mealie_{DB_VERSION}.sqlite")
|
||||
|
||||
else:
|
||||
raise Exception(
|
||||
"Unable to determine database type. Acceptible options are 'mongo' or 'tinydb' "
|
||||
"Unable to determine database type. Acceptible options are 'sqlite' "
|
||||
)
|
||||
|
||||
# Mongo Database
|
||||
@@ -72,6 +82,3 @@ DB_PORT = os.getenv("db_port", 27017)
|
||||
# SFTP Email Stuff - For use Later down the line!
|
||||
SFTP_USERNAME = os.getenv("sftp_username", None)
|
||||
SFTP_PASSWORD = os.getenv("sftp_password", None)
|
||||
|
||||
|
||||
ensure_dirs()
|
||||
|
||||
@@ -9,16 +9,15 @@ from db.sql.theme_models import SiteThemeModel
|
||||
"""
|
||||
# TODO
|
||||
- [ ] Abstract Classes to use save_new, and update from base models
|
||||
- [x] Create Category and Tags Table with Many to Many relationship
|
||||
"""
|
||||
|
||||
|
||||
class _Recipes(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "slug"
|
||||
self.sql_model = RecipeModel
|
||||
self.sql_model: RecipeModel = RecipeModel
|
||||
|
||||
def update_image(self, session: Session, slug: str, extension: str) -> str:
|
||||
def update_image(self, session: Session, slug: str, extension: str = None) -> str:
|
||||
entry: RecipeModel = self._query_one(session, match_value=slug)
|
||||
entry.image = f"{slug}.{extension}"
|
||||
session.commit()
|
||||
@@ -49,14 +48,6 @@ class _Settings(BaseDocument):
|
||||
self.primary_key = "name"
|
||||
self.sql_model = SiteSettingsModel
|
||||
|
||||
def save_new(self, session: Session, main: dict, webhooks: dict) -> str:
|
||||
new_settings = self.sql_model(main.get("name"), webhooks)
|
||||
|
||||
session.add(new_settings)
|
||||
session.commit()
|
||||
|
||||
return new_settings.dict()
|
||||
|
||||
|
||||
class _Themes(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
|
||||
@@ -100,13 +100,19 @@ class BaseDocument:
|
||||
match_key = self.primary_key
|
||||
|
||||
result = (
|
||||
session.query(self.sql_model).filter_by(**{match_key: match_value}).one()
|
||||
session.query(self.sql_model)
|
||||
.filter_by(**{match_key: match_value})
|
||||
.limit(limit)
|
||||
.all()
|
||||
)
|
||||
db_entry = result.dict()
|
||||
db_entries = [x.dict() for x in result]
|
||||
|
||||
return db_entry
|
||||
if limit == 1:
|
||||
return db_entries[0]
|
||||
|
||||
def save_new(self, session: Session, document: dict) -> dict:
|
||||
return db_entries
|
||||
|
||||
def create(self, session: Session, document: dict) -> dict:
|
||||
"""Creates a new database entry for the given SQL Alchemy Model.
|
||||
|
||||
Args: \n
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user