mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2025-12-23 18:29:23 -05:00
Merge branch 'TandoorRecipes:develop' into cookbookapp-images-import
This commit is contained in:
@@ -6,8 +6,10 @@ class StyleTreeprocessor(Treeprocessor):
|
||||
|
||||
def run_processor(self, node):
|
||||
for child in node:
|
||||
# if child.tag == "table":
|
||||
# child.set("class", "markdown-body")
|
||||
if child.tag == "table":
|
||||
child.set("class", "markdown-table")
|
||||
if child.tag == "th" or child.tag == "td":
|
||||
child.set("class", "markdown-table-cell")
|
||||
if child.tag == "img":
|
||||
child.set("class", "img-fluid")
|
||||
self.run_processor(child)
|
||||
|
||||
@@ -341,7 +341,7 @@ class OpenDataImporter:
|
||||
obj_dict = {
|
||||
'name': self.data[datatype][k]['name'],
|
||||
'plural_name': self.data[datatype][k]['plural_name'] if self.data[datatype][k]['plural_name'] != '' else None,
|
||||
'supermarket_category_id': self.slug_id_cache['category'][self.data[datatype][k]['store_category']],
|
||||
'supermarket_category_id': self.slug_id_cache['category'][self.data[datatype][k]['store_category']] if self.data[datatype][k]['store_category'] in self.slug_id_cache['category'] else None,
|
||||
'fdc_id': re.sub(r'\D', '', self.data[datatype][k]['fdc_id']) if self.data[datatype][k]['fdc_id'] != '' else None,
|
||||
'open_data_slug': k,
|
||||
'properties_food_unit_id': None,
|
||||
|
||||
@@ -641,7 +641,7 @@ class SupermarketCategorySerializer(UniqueFieldsMixin, WritableNestedModelSerial
|
||||
|
||||
class Meta:
|
||||
model = SupermarketCategory
|
||||
fields = ('id', 'name', 'description')
|
||||
fields = ('id', 'name', 'description', 'open_data_slug')
|
||||
|
||||
|
||||
class SupermarketCategoryRelationSerializer(WritableNestedModelSerializer):
|
||||
@@ -729,6 +729,7 @@ class RecipeFlatSerializer(WritableNestedModelSerializer):
|
||||
class Meta:
|
||||
model = Recipe
|
||||
fields = ('id', 'name', 'image')
|
||||
read_only_fields = ('id', 'name', 'image')
|
||||
|
||||
|
||||
class FoodSimpleSerializer(serializers.ModelSerializer):
|
||||
@@ -1772,7 +1773,7 @@ class AiImportSerializer(serializers.Serializer):
|
||||
class ExportRequestSerializer(serializers.Serializer):
|
||||
type = serializers.CharField()
|
||||
all = serializers.BooleanField(default=False)
|
||||
recipes = RecipeFlatSerializer(many=True, default=[])
|
||||
recipes = RecipeSimpleSerializer(many=True, default=[])
|
||||
custom_filter = CustomFilterSerializer(many=False, default=None, allow_null=True)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
from django.test import utils
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
# disables scoping error in all queries used inside the test FUNCTIONS
|
||||
# FIXTURES need to have their own scopes_disabled!!
|
||||
# This is done by hook pytest_fixture_setup in conftest.py for all non yield fixtures
|
||||
utils.setup_databases = scopes_disabled()(utils.setup_databases)
|
||||
@@ -1,75 +0,0 @@
|
||||
import pytest
|
||||
from django.contrib import auth
|
||||
from django.contrib import messages
|
||||
from django.contrib.messages import get_messages
|
||||
from django.urls import reverse
|
||||
from pytest_django.asserts import assertTemplateUsed
|
||||
|
||||
from cookbook.models import ConnectorConfig
|
||||
|
||||
EDIT_VIEW_NAME = 'edit_connector_config'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def home_assistant_config_obj(a1_s1, space_1):
|
||||
return ConnectorConfig.objects.create(
|
||||
name='HomeAssistant 1',
|
||||
type=ConnectorConfig.HOMEASSISTANT,
|
||||
token='token',
|
||||
url='http://localhost:8123/api',
|
||||
todo_entity='todo.shopping_list',
|
||||
enabled=True,
|
||||
created_by=auth.get_user(a1_s1),
|
||||
space=space_1,
|
||||
)
|
||||
|
||||
|
||||
def test_edit_connector_config_homeassistant(home_assistant_config_obj: ConnectorConfig, a1_s1, a1_s2):
|
||||
new_token = '1234_token'
|
||||
|
||||
r = a1_s1.post(
|
||||
reverse(EDIT_VIEW_NAME, args={home_assistant_config_obj.pk}),
|
||||
{
|
||||
'name': home_assistant_config_obj.name,
|
||||
'type': home_assistant_config_obj.type,
|
||||
'url': home_assistant_config_obj.url,
|
||||
'update_token': new_token,
|
||||
'todo_entity': home_assistant_config_obj.todo_entity,
|
||||
'enabled': home_assistant_config_obj.enabled,
|
||||
}
|
||||
)
|
||||
assert r.status_code == 302
|
||||
|
||||
r_messages = [m for m in get_messages(r.wsgi_request)]
|
||||
assert not any(m.level > messages.SUCCESS for m in r_messages)
|
||||
|
||||
home_assistant_config_obj.refresh_from_db()
|
||||
assert home_assistant_config_obj.token == new_token
|
||||
|
||||
r = a1_s2.post(
|
||||
reverse(EDIT_VIEW_NAME, args={home_assistant_config_obj.pk}),
|
||||
{
|
||||
'name': home_assistant_config_obj.name,
|
||||
'type': home_assistant_config_obj.type,
|
||||
'url': home_assistant_config_obj.url,
|
||||
'todo_entity': home_assistant_config_obj.todo_entity,
|
||||
'update_token': new_token,
|
||||
'enabled': home_assistant_config_obj.enabled,
|
||||
}
|
||||
)
|
||||
assert r.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"arg", [
|
||||
['a_u', 302],
|
||||
['g1_s1', 302],
|
||||
['u1_s1', 302],
|
||||
['a1_s1', 200],
|
||||
['g1_s2', 302],
|
||||
['u1_s2', 302],
|
||||
['a1_s2', 404],
|
||||
])
|
||||
def test_view_permission(arg, request, home_assistant_config_obj):
|
||||
c = request.getfixturevalue(arg[0])
|
||||
assert c.get(reverse(EDIT_VIEW_NAME, args={home_assistant_config_obj.pk})).status_code == arg[1]
|
||||
@@ -1,64 +0,0 @@
|
||||
import pytest
|
||||
from django.contrib import auth
|
||||
from django.contrib import messages
|
||||
from django.contrib.messages import get_messages
|
||||
from django.urls import reverse
|
||||
|
||||
from cookbook.models import Storage
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def storage_obj(a1_s1, space_1):
|
||||
return Storage.objects.create(
|
||||
name='TestStorage',
|
||||
method=Storage.DROPBOX,
|
||||
created_by=auth.get_user(a1_s1),
|
||||
token='test',
|
||||
username='test',
|
||||
password='test',
|
||||
space=space_1,
|
||||
)
|
||||
|
||||
|
||||
def test_edit_storage(storage_obj, a1_s1, a1_s2):
|
||||
r = a1_s1.post(
|
||||
reverse('edit_storage', args={storage_obj.pk}),
|
||||
{
|
||||
'name': 'NewStorage',
|
||||
'password': '1234_pw',
|
||||
'token': '1234_token',
|
||||
'method': Storage.DROPBOX
|
||||
}
|
||||
)
|
||||
storage_obj.refresh_from_db()
|
||||
assert r.status_code == 302
|
||||
#r_messages = [m for m in get_messages(r.wsgi_request)]
|
||||
#assert not any(m.level > messages.SUCCESS for m in r_messages)
|
||||
|
||||
#assert storage_obj.password == '1234_pw'
|
||||
#assert storage_obj.token == '1234_token'
|
||||
|
||||
r = a1_s2.post(
|
||||
reverse('edit_storage', args={storage_obj.pk}),
|
||||
{
|
||||
'name': 'NewStorage',
|
||||
'password': '1234_pw',
|
||||
'token': '1234_token',
|
||||
'method': Storage.DROPBOX
|
||||
}
|
||||
)
|
||||
assert r.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.parametrize("arg", [
|
||||
['a_u', 302],
|
||||
['g1_s1', 302],
|
||||
['u1_s1', 302],
|
||||
['a1_s1', 302],
|
||||
['g1_s2', 302],
|
||||
['u1_s2', 302],
|
||||
['a1_s2', 404],
|
||||
])
|
||||
def test_view_permission(arg, request, storage_obj):
|
||||
c = request.getfixturevalue(arg[0])
|
||||
assert c.get(reverse('edit_storage', args={storage_obj.pk})).status_code == arg[1]
|
||||
@@ -1,24 +0,0 @@
|
||||
import pytest
|
||||
from django.contrib import auth
|
||||
from django.urls import reverse
|
||||
|
||||
from cookbook.forms import ImportExportBase
|
||||
from cookbook.models import ExportLog
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def obj_1(space_1, u1_s1):
|
||||
return ExportLog.objects.create(type=ImportExportBase.DEFAULT, running=False, created_by=auth.get_user(u1_s1), space=space_1, exported_recipes=10, total_recipes=10)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("arg", [
|
||||
['a_u', 302],
|
||||
['g1_s1', 302],
|
||||
['u1_s1', 200],
|
||||
['a1_s1', 200],
|
||||
['u1_s2', 404],
|
||||
['a1_s2', 404],
|
||||
])
|
||||
def test_export_file_cache(arg, request, obj_1):
|
||||
c = request.getfixturevalue(arg[0])
|
||||
assert c.get(reverse('view_export_file', args=[obj_1.pk])).status_code == arg[1]
|
||||
@@ -120,7 +120,7 @@ urlpatterns = [
|
||||
path('api-token-auth/', CustomAuthToken.as_view()),
|
||||
|
||||
path('offline/', views.offline, name='view_offline'),
|
||||
path('service-worker.js', (TemplateView.as_view(template_name="sw.js", content_type='application/javascript')), name='service_worker'),
|
||||
#path('service-worker.js', (TemplateView.as_view(template_name="sw.js", content_type='application/javascript')), name='service_worker'),
|
||||
path('manifest.json', views.web_manifest, name='web_manifest'),
|
||||
|
||||
]
|
||||
|
||||
@@ -83,6 +83,8 @@ def get_integration(request, export_type):
|
||||
return Rezeptsuitede(request, export_type)
|
||||
if export_type == ImportExportBase.GOURMET:
|
||||
return Gourmet(request, export_type)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def export_file(request, pk):
|
||||
el = get_object_or_404(ExportLog, pk=pk, space=request.space)
|
||||
@@ -92,7 +94,7 @@ def export_file(request, pk):
|
||||
if cacheData is None:
|
||||
el.possibly_not_expired = False
|
||||
el.save()
|
||||
return render(request, 'export_response.html', {'pk': pk})
|
||||
return JsonResponse({'msg': 'Export Expired or not found'}, status=404)
|
||||
|
||||
response = HttpResponse(cacheData['file'], content_type='application/force-download')
|
||||
response['Content-Disposition'] = 'attachment; filename="' + cacheData['filename'] + '"'
|
||||
|
||||
@@ -20,7 +20,7 @@ UNINSTALL_MIDDLEWARE = [
|
||||
]
|
||||
|
||||
UNINSTALL_INSTALLED_APPS = [
|
||||
'django.contrib.messages', 'django.contrib.sites', 'django.contrib.staticfiles', 'corsheaders', 'django_cleanup.apps.CleanupConfig', 'django_js_reverse', 'hcaptcha']
|
||||
'django.contrib.messages', 'django.contrib.sites', 'django.contrib.staticfiles', 'corsheaders', 'django_cleanup.apps.CleanupConfig', 'hcaptcha']
|
||||
|
||||
# disable extras not needed for testing
|
||||
for x in UNINSTALL_MIDDLEWARE:
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Django==4.2.22
|
||||
cryptography===44.0.1
|
||||
cryptography===45.0.5
|
||||
django-annoying==0.10.6
|
||||
django-cleanup==9.0.0
|
||||
django-crispy-forms==2.4
|
||||
crispy-bootstrap4==2025.6
|
||||
djangorestframework==3.15.2
|
||||
drf-spectacular==0.27.1
|
||||
drf-spectacular-sidecar==2024.2.1
|
||||
drf-spectacular-sidecar==2025.7.1
|
||||
drf-writable-nested==0.7.2
|
||||
django-oauth-toolkit==2.4.0
|
||||
django-debug-toolbar==4.3.0
|
||||
@@ -40,7 +40,7 @@ django-hCaptcha==0.2.0
|
||||
python-ldap==3.4.4
|
||||
django-auth-ldap==4.6.0
|
||||
pyppeteer==2.0.0
|
||||
pytubefix==8.13.1
|
||||
pytubefix==9.2.2
|
||||
aiohttp==3.10.11
|
||||
inflection==0.5.1
|
||||
redis==5.2.1
|
||||
@@ -48,17 +48,17 @@ requests-oauthlib==2.0.0
|
||||
pyjwt==2.10.1
|
||||
python3-openid==3.2.0
|
||||
python3-saml==1.16.0
|
||||
django-vite==3.0.3
|
||||
django-vite==3.1.0
|
||||
litellm==1.64.1
|
||||
|
||||
# Development
|
||||
pytest==8.0.0
|
||||
pytest-django==4.10.0
|
||||
pytest-cov===5.0.0
|
||||
pytest-factoryboy==2.7.0
|
||||
pytest==8.4.1
|
||||
pytest-django==4.11.0
|
||||
pytest-cov===6.0.0
|
||||
pytest-factoryboy==2.8.0
|
||||
pytest-html==4.1.1
|
||||
pytest-asyncio==0.23.5
|
||||
pytest-xdist==3.6.1
|
||||
pytest-asyncio==0.25.3
|
||||
pytest-xdist==3.7.0
|
||||
autopep8==2.3.2
|
||||
flake8==7.3.0
|
||||
yapf==0.40.2
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"build": "vite build --emptyOutDir",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -18,32 +18,32 @@
|
||||
"pinia": "^3.0.2",
|
||||
"vue": "^3.5.13",
|
||||
"vue-draggable-plus": "^0.6.0",
|
||||
"vue-i18n": "^11.1.3",
|
||||
"vue-i18n": "^11.1.7",
|
||||
"vue-router": "^4.5.0",
|
||||
"vue-simple-calendar": "7.1.0",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
"vuetify": "^3.8.10"
|
||||
"vuetify": "^3.8.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.7.2",
|
||||
"@tsconfig/node22": "^22.0.1",
|
||||
"@types/jsdom": "^21.1.7",
|
||||
"@types/node": "^22.14.1",
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"@types/node": "^24.0.8",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"jsdom": "^26.1.0",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "6.3.5",
|
||||
"vite-plugin-pwa": "^1.0.0",
|
||||
"vite-plugin-pwa": "^1.0.1",
|
||||
"workbox-build": "^7.3.0",
|
||||
"workbox-window": "^7.3.0",
|
||||
"workbox-background-sync": "^7.0.0",
|
||||
"workbox-expiration": "^7.0.0",
|
||||
"workbox-navigation-preload": "^7.0.0",
|
||||
"workbox-precaching": "^7.0.0",
|
||||
"workbox-routing": "^7.0.0",
|
||||
"workbox-strategies": "^6.2.4",
|
||||
"workbox-background-sync": "^7.3.0",
|
||||
"workbox-expiration": "^7.3.0",
|
||||
"workbox-navigation-preload": "^7.3.0",
|
||||
"workbox-precaching": "^7.3.0",
|
||||
"workbox-routing": "^7.3.0",
|
||||
"workbox-strategies": "^7.3.0",
|
||||
"vite-plugin-vuetify": "^2.1.1",
|
||||
"vue-tsc": "^2.2.8"
|
||||
}
|
||||
|
||||
52
vue3/src/components/dialogs/HelpDialog.vue
Normal file
52
vue3/src/components/dialogs/HelpDialog.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
|
||||
<v-dialog height="70vh" activator="parent">
|
||||
<v-card>
|
||||
<v-closable-card-title v-model="dialog" :title="$t('Help')" icon="fa-solid fa-question"></v-closable-card-title>
|
||||
<v-divider></v-divider>
|
||||
<v-card-text class="pa-0">
|
||||
<v-layout style="height: 100%">
|
||||
<v-navigation-drawer style="height: calc(100% + 0px)">
|
||||
<v-list-item>
|
||||
<v-text-field density="compact" variant="outlined" class="pt-2 pb-2" :label="$t('Search')" hide-details clearable ></v-text-field>
|
||||
</v-list-item>
|
||||
<v-divider></v-divider>
|
||||
<v-list-item link title="Start" @click="window = 'start'"></v-list-item>
|
||||
<v-list-item link title="Test" @click="window = 'test'"></v-list-item>
|
||||
|
||||
</v-navigation-drawer>
|
||||
|
||||
<v-main>
|
||||
<v-container>
|
||||
<v-window v-model="window">
|
||||
<v-window-item value="start">
|
||||
Start
|
||||
</v-window-item>
|
||||
<v-window-item value="test">
|
||||
Test
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</v-container>
|
||||
</v-main>
|
||||
</v-layout>
|
||||
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
|
||||
import {ref} from "vue";
|
||||
|
||||
const dialog = ref(false)
|
||||
const window = ref('start')
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -52,4 +52,38 @@ p, ol, ul, li {
|
||||
margin: revert;
|
||||
}
|
||||
|
||||
|
||||
/* css classes needed to render markdown blockquotes */
|
||||
blockquote {
|
||||
background: rgb(200,200,200,0.2);
|
||||
border-left: 4px solid #ccc;
|
||||
margin: 1.5em 10px;
|
||||
padding: .5em 10px;
|
||||
quotes: none;
|
||||
}
|
||||
|
||||
blockquote:before {
|
||||
color: #ccc;
|
||||
content: open-quote;
|
||||
font-size: 4em;
|
||||
line-height: .1em;
|
||||
margin-right: .25em;
|
||||
vertical-align: -.4em;
|
||||
}
|
||||
|
||||
blockquote p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.markdown-table {
|
||||
border: 1px solid;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.markdown-table-cell {
|
||||
border: 1px solid;
|
||||
border-collapse: collapse;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -79,6 +79,14 @@
|
||||
<v-window v-model="currentTab">
|
||||
<v-window-item value="shopping">
|
||||
<v-container>
|
||||
<v-row class="pa-0" dense>
|
||||
<v-col class="pa-0">
|
||||
<v-chip-group v-model="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket" v-if="supermarkets.length > 0" >
|
||||
<v-chip v-for="s in supermarkets" :value="s" :key="s.id" label density="compact" variant="outlined" color="primary">{{s.name}}</v-chip>
|
||||
</v-chip-group>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-alert v-if="useShoppingStore().hasFailedItems()" color="warning" class="mb-2">
|
||||
@@ -226,20 +234,18 @@
|
||||
|
||||
import {computed, onMounted, ref} from "vue";
|
||||
import {useShoppingStore} from "@/stores/ShoppingStore";
|
||||
import {ApiApi, IngredientString, ResponseError, ShoppingListEntry, ShoppingListRecipe, Supermarket} from "@/openapi";
|
||||
import {ApiApi, ResponseError, ShoppingListEntry, ShoppingListRecipe, Supermarket} from "@/openapi";
|
||||
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
|
||||
import ShoppingLineItem from "@/components/display/ShoppingLineItem.vue";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||
import ShoppingLineItemDialog from "@/components/dialogs/ShoppingLineItemDialog.vue";
|
||||
import {IShoppingListCategory, IShoppingListFood, ShoppingGroupingOptions} from "@/types/Shopping";
|
||||
import {ShoppingGroupingOptions} from "@/types/Shopping";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import NumberScalerDialog from "@/components/inputs/NumberScalerDialog.vue";
|
||||
import SupermarketEditor from "@/components/model_editors/SupermarketEditor.vue";
|
||||
import DeleteConfirmDialog from "@/components/dialogs/DeleteConfirmDialog.vue";
|
||||
import ShoppingListEntryInput from "@/components/inputs/ShoppingListEntryInput.vue";
|
||||
import {DateTime} from "luxon";
|
||||
import MealPlanEditor from "@/components/model_editors/MealPlanEditor.vue";
|
||||
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
|
||||
import {onBeforeRouteLeave} from "vue-router";
|
||||
import {isShoppingCategoryVisible} from "@/utils/logic_utils.ts";
|
||||
@@ -248,6 +254,7 @@ import ShoppingExportDialog from "@/components/dialogs/ShoppingExportDialog.vue"
|
||||
const {t} = useI18n()
|
||||
|
||||
const currentTab = ref("shopping")
|
||||
const supermarkets = ref([] as Supermarket[])
|
||||
|
||||
/**
|
||||
* VSelect items for shopping list grouping options with localized names
|
||||
@@ -279,6 +286,8 @@ onMounted(() => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
loadSupermarkets()
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -339,6 +348,20 @@ function deleteListRecipe(slr: ShoppingListRecipe) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* load a list of supermarkets
|
||||
*/
|
||||
function loadSupermarkets(){
|
||||
let api = new ApiApi()
|
||||
|
||||
api.apiSupermarketList().then(r => {
|
||||
supermarkets.value = r.results
|
||||
// TODO either recursive or add a "favorite" attribute to supermarkets for them to display at all
|
||||
}).catch(err => {
|
||||
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<v-progress-linear :model-value="timerProgress" color="primary" height="5"></v-progress-linear>
|
||||
<v-alert :color="timerColor" class="rounded-0" variant="tonal">
|
||||
<v-alert-title><i class="fas fa-stopwatch mr-1"></i> {{ Duration.fromMillis(durationSeconds * 1000).toFormat('hh:mm:ss') }}</v-alert-title>
|
||||
{{$t('FinishedAt')}} {{ DateTime.now().plus({'seconds': durationSeconds}).toLocaleString(DateTime.TIME_SIMPLE) }}
|
||||
{{$t('FinishedAt')}} {{ formatFinishTime(durationSeconds) }}
|
||||
<template #close>
|
||||
<v-btn-group divided>
|
||||
<v-btn width="40" @click="changeTimer(-60)"><i class="fas fa-minus"></i>1</v-btn>
|
||||
@@ -76,8 +76,26 @@ function stopTimer() {
|
||||
emit('stop')
|
||||
}
|
||||
|
||||
/**
|
||||
* formats a future time based on a duration in seconds from now
|
||||
* displays the time in "hh:mm" format and adds a "+Xd" suffix if the target day differs from today
|
||||
*/
|
||||
function formatFinishTime(durationSeconds: number): string {
|
||||
const now = DateTime.now()
|
||||
const target = now.plus({ seconds: durationSeconds })
|
||||
let timeString = target.toLocaleString(DateTime.TIME_SIMPLE)
|
||||
const daysDifference = Math.floor(
|
||||
target.startOf('day').diff(now.startOf('day'), 'days').days
|
||||
)
|
||||
if (daysDifference >= 1) {
|
||||
const label = daysDifference === 1 ? t('Day') : t('Days');
|
||||
timeString += ` +${daysDifference} ${label}`;
|
||||
}
|
||||
return timeString
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<v-label>{{ $t('Ingredients') }}</v-label>
|
||||
<div v-if="!mobile">
|
||||
<vue-draggable v-model="step.ingredients" handle=".drag-handle" :on-sort="sortIngredients" :empty-insert-threshold="25" group="ingredients">
|
||||
<v-row v-for="(ingredient, index) in step.ingredients" dense>
|
||||
<v-row v-for="(ingredient, index) in step.ingredients" :key="ingredient.id" dense>
|
||||
<v-col cols="2" v-if="!ingredient.isHeader">
|
||||
<v-input hide-details>
|
||||
<template #prepend>
|
||||
@@ -118,7 +118,7 @@
|
||||
|
||||
<v-list v-if="mobile">
|
||||
<vue-draggable v-model="step.ingredients" handle=".drag-handle" :on-sort="sortIngredients" group="ingredients" empty-insert-threshold="25">
|
||||
<v-list-item v-for="(ingredient, index) in step.ingredients" border @click="editingIngredientIndex = index; dialogIngredientEditor = true">
|
||||
<v-list-item v-for="(ingredient, index) in step.ingredients" :key="ingredient.id" border @click="editingIngredientIndex = index; dialogIngredientEditor = true">
|
||||
<ingredient-string :ingredient="ingredient"></ingredient-string>
|
||||
<template #append>
|
||||
<v-icon icon="$dragHandle" class="drag-handle"></v-icon>
|
||||
@@ -344,6 +344,7 @@ function insertAndFocusIngredient() {
|
||||
|
||||
step.value.ingredients.push(ingredient)
|
||||
nextTick(() => {
|
||||
sortIngredients()
|
||||
if (mobile.value) {
|
||||
editingIngredientIndex.value = step.value.ingredients.length - 1
|
||||
dialogIngredientEditor.value = true
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -14,6 +14,7 @@
|
||||
|
||||
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
|
||||
<v-textarea :label="$t('Description')" v-model="editingObj.description"></v-textarea>
|
||||
<v-text-field :label="$t('Open_Data_Slug')" :hint="$t('open_data_help_text')" persistent-hint v-model="editingObj.openDataSlug" disabled></v-text-field>
|
||||
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
|
||||
@@ -19,12 +19,12 @@ import {
|
||||
CustomFilterFromJSONTyped,
|
||||
CustomFilterToJSON,
|
||||
} from './CustomFilter';
|
||||
import type { RecipeFlat } from './RecipeFlat';
|
||||
import type { RecipeSimple } from './RecipeSimple';
|
||||
import {
|
||||
RecipeFlatFromJSON,
|
||||
RecipeFlatFromJSONTyped,
|
||||
RecipeFlatToJSON,
|
||||
} from './RecipeFlat';
|
||||
RecipeSimpleFromJSON,
|
||||
RecipeSimpleFromJSONTyped,
|
||||
RecipeSimpleToJSON,
|
||||
} from './RecipeSimple';
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -46,10 +46,10 @@ export interface ExportRequest {
|
||||
all?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {Array<RecipeFlat>}
|
||||
* @type {Array<RecipeSimple>}
|
||||
* @memberof ExportRequest
|
||||
*/
|
||||
recipes?: Array<RecipeFlat>;
|
||||
recipes?: Array<RecipeSimple>;
|
||||
/**
|
||||
*
|
||||
* @type {CustomFilter}
|
||||
@@ -78,7 +78,7 @@ export function ExportRequestFromJSONTyped(json: any, ignoreDiscriminator: boole
|
||||
|
||||
'type': json['type'],
|
||||
'all': json['all'] == null ? undefined : json['all'],
|
||||
'recipes': json['recipes'] == null ? undefined : ((json['recipes'] as Array<any>).map(RecipeFlatFromJSON)),
|
||||
'recipes': json['recipes'] == null ? undefined : ((json['recipes'] as Array<any>).map(RecipeSimpleFromJSON)),
|
||||
'customFilter': json['custom_filter'] == null ? undefined : CustomFilterFromJSON(json['custom_filter']),
|
||||
};
|
||||
}
|
||||
@@ -91,7 +91,7 @@ export function ExportRequestToJSON(value?: ExportRequest | null): any {
|
||||
|
||||
'type': value['type'],
|
||||
'all': value['all'],
|
||||
'recipes': value['recipes'] == null ? undefined : ((value['recipes'] as Array<any>).map(RecipeFlatToJSON)),
|
||||
'recipes': value['recipes'] == null ? undefined : ((value['recipes'] as Array<any>).map(RecipeSimpleToJSON)),
|
||||
'custom_filter': CustomFilterToJSON(value['customFilter']),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -71,6 +71,12 @@ export interface PatchedSupermarketCategory {
|
||||
* @memberof PatchedSupermarketCategory
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PatchedSupermarketCategory
|
||||
*/
|
||||
openDataSlug?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,6 +99,7 @@ export function PatchedSupermarketCategoryFromJSONTyped(json: any, ignoreDiscrim
|
||||
'id': json['id'] == null ? undefined : json['id'],
|
||||
'name': json['name'] == null ? undefined : json['name'],
|
||||
'description': json['description'] == null ? undefined : json['description'],
|
||||
'openDataSlug': json['open_data_slug'] == null ? undefined : json['open_data_slug'],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -105,6 +112,7 @@ export function PatchedSupermarketCategoryToJSON(value?: PatchedSupermarketCateg
|
||||
'id': value['id'],
|
||||
'name': value['name'],
|
||||
'description': value['description'],
|
||||
'open_data_slug': value['openDataSlug'],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -30,13 +30,13 @@ export interface RecipeFlat {
|
||||
* @type {string}
|
||||
* @memberof RecipeFlat
|
||||
*/
|
||||
name: string;
|
||||
readonly name: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RecipeFlat
|
||||
*/
|
||||
image?: string;
|
||||
readonly image: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,6 +44,7 @@ export interface RecipeFlat {
|
||||
*/
|
||||
export function instanceOfRecipeFlat(value: object): value is RecipeFlat {
|
||||
if (!('name' in value) || value['name'] === undefined) return false;
|
||||
if (!('image' in value) || value['image'] === undefined) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -59,19 +60,17 @@ export function RecipeFlatFromJSONTyped(json: any, ignoreDiscriminator: boolean)
|
||||
|
||||
'id': json['id'] == null ? undefined : json['id'],
|
||||
'name': json['name'],
|
||||
'image': json['image'] == null ? undefined : json['image'],
|
||||
'image': json['image'],
|
||||
};
|
||||
}
|
||||
|
||||
export function RecipeFlatToJSON(value?: RecipeFlat | null): any {
|
||||
export function RecipeFlatToJSON(value?: Omit<RecipeFlat, 'name'|'image'> | null): any {
|
||||
if (value == null) {
|
||||
return value;
|
||||
}
|
||||
return {
|
||||
|
||||
'id': value['id'],
|
||||
'name': value['name'],
|
||||
'image': value['image'],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,12 @@ export interface SupermarketCategory {
|
||||
* @memberof SupermarketCategory
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SupermarketCategory
|
||||
*/
|
||||
openDataSlug?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,6 +100,7 @@ export function SupermarketCategoryFromJSONTyped(json: any, ignoreDiscriminator:
|
||||
'id': json['id'] == null ? undefined : json['id'],
|
||||
'name': json['name'],
|
||||
'description': json['description'] == null ? undefined : json['description'],
|
||||
'openDataSlug': json['open_data_slug'] == null ? undefined : json['open_data_slug'],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -106,6 +113,7 @@ export function SupermarketCategoryToJSON(value?: SupermarketCategory | null): a
|
||||
'id': value['id'],
|
||||
'name': value['name'],
|
||||
'description': value['description'],
|
||||
'open_data_slug': value['openDataSlug'],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-btn>
|
||||
Test Dialog
|
||||
<help-dialog></help-dialog>
|
||||
</v-btn>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-expansion-panels>
|
||||
@@ -90,6 +95,7 @@
|
||||
|
||||
import {TKeyword, TRecipe, TUnit, TUserSpace} from "@/types/Models";
|
||||
import {useUserPreferenceStore} from "../stores/UserPreferenceStore";
|
||||
import HelpDialog from "@/components/dialogs/HelpDialog.vue";
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
|
||||
<v-stepper-actions>
|
||||
<template #prev>
|
||||
<v-btn @click="stepper = 'type'">{{ $t('Back') }}</v-btn>
|
||||
<v-btn @click="stepper = 'type'; importResponse = {}">{{ $t('Back') }}</v-btn>
|
||||
</template>
|
||||
<template #next>
|
||||
<v-btn @click="loadRecipeFromUrl({url: importUrl})" v-if="importType == 'url'" :disabled="importUrl == ''" :loading="loading">{{
|
||||
|
||||
105
vue3/yarn.lock
105
vue3/yarn.lock
@@ -959,26 +959,26 @@
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.2.tgz#8249de9b7e22fcb3ceb5e66090c30a1d5492b81a"
|
||||
integrity sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA==
|
||||
|
||||
"@intlify/core-base@11.1.6":
|
||||
version "11.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-11.1.6.tgz#87986c85e45a973e810296b046a77c0ae88f8130"
|
||||
integrity sha512-gfMLnoWGiQkA1BwK6Qbrog/e3I6Lnkhqk08XObJb0lMq6sLG1Ggl2MazVaMfGnv/E1Td8pCS5UwR54Ys+fOxmQ==
|
||||
"@intlify/core-base@11.1.7":
|
||||
version "11.1.7"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-11.1.7.tgz#497280e4774011cf0d42eaedb20e9cd4594c0a3f"
|
||||
integrity sha512-gYiGnQeJVp3kNBeXQ73m1uFOak0ry4av8pn+IkEWigyyPWEMGzB+xFeQdmGMFn49V+oox6294oGVff8bYOhtOw==
|
||||
dependencies:
|
||||
"@intlify/message-compiler" "11.1.6"
|
||||
"@intlify/shared" "11.1.6"
|
||||
"@intlify/message-compiler" "11.1.7"
|
||||
"@intlify/shared" "11.1.7"
|
||||
|
||||
"@intlify/message-compiler@11.1.6":
|
||||
version "11.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-11.1.6.tgz#65a86f9c7b030b4c2d7faa4ab4f9104adbd1fdbf"
|
||||
integrity sha512-w0LYo5sqgQZF3vEmjLlx+5PYk5EEiB+uigsBkka/DKoAIH2c5xlXcjAxhTgSw35Vrck+GOGriahFsfbHL+ZjPw==
|
||||
"@intlify/message-compiler@11.1.7":
|
||||
version "11.1.7"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-11.1.7.tgz#047ba659cfd34b0f630dddf73c3f9224bd3af7f8"
|
||||
integrity sha512-0ezkep1AT30NyuKj8QbRlmvMORCCRlOIIu9v8RNU8SwDjjTiFCZzczCORMns2mCH4HZ1nXgrfkKzYUbfjNRmng==
|
||||
dependencies:
|
||||
"@intlify/shared" "11.1.6"
|
||||
"@intlify/shared" "11.1.7"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
"@intlify/shared@11.1.6":
|
||||
version "11.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-11.1.6.tgz#57887e58559c229773da792a97506e16d41042fa"
|
||||
integrity sha512-G1Pe4UILhiGOItuehRW+Pk9/NlnRaMFsdnhZ1fwBjiHvrzitmPNZdLx7Eo3GPfRrsk1mdkilZSfgH8SnM419vA==
|
||||
"@intlify/shared@11.1.7":
|
||||
version "11.1.7"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-11.1.7.tgz#54e60d52b73fb25019e2689d6531a54928b40194"
|
||||
integrity sha512-4yZeMt2Aa/7n5Ehy4KalUlvt3iRLcg1tq9IBVfOgkyWFArN4oygn6WxgGIFibP3svpaH8DarbNaottq+p0gUZQ==
|
||||
|
||||
"@jridgewell/gen-mapping@^0.3.5":
|
||||
version "0.3.8"
|
||||
@@ -1020,6 +1020,11 @@
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@rolldown/pluginutils@1.0.0-beta.19":
|
||||
version "1.0.0-beta.19"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz#fc3b95145a8e7a3bf92754269d8e4f40eea8a244"
|
||||
integrity sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==
|
||||
|
||||
"@rollup/plugin-babel@^5.2.0":
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"
|
||||
@@ -1213,20 +1218,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.6.2.tgz#be6536931801f437eafcb9c0f6d6781f72308041"
|
||||
integrity sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==
|
||||
|
||||
"@types/node@*":
|
||||
version "24.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-24.0.3.tgz#f935910f3eece3a3a2f8be86b96ba833dc286cab"
|
||||
integrity sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==
|
||||
"@types/node@*", "@types/node@^24.0.8":
|
||||
version "24.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-24.0.8.tgz#98f50977fe76ab78d02fc3bcb7f6c3b5f79a363c"
|
||||
integrity sha512-WytNrFSgWO/esSH9NbpWUfTMGQwCGIKfCmNlmFDNiI5gGhgMmEA+V1AEvKLeBNvvtBnailJtkrEa2OIISwrVAA==
|
||||
dependencies:
|
||||
undici-types "~7.8.0"
|
||||
|
||||
"@types/node@^22.14.1":
|
||||
version "22.15.32"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.32.tgz#c301cc2275b535a5e54bb81d516b1d2e9afe06e5"
|
||||
integrity sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA==
|
||||
dependencies:
|
||||
undici-types "~6.21.0"
|
||||
|
||||
"@types/resolve@1.20.2":
|
||||
version "1.20.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975"
|
||||
@@ -1252,10 +1250,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz#525433c784aed9b457aaa0ee3d92aeb71f346b63"
|
||||
integrity sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==
|
||||
|
||||
"@vitejs/plugin-vue@^5.2.3":
|
||||
version "5.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz#9e8a512eb174bfc2a333ba959bbf9de428d89ad8"
|
||||
integrity sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==
|
||||
"@vitejs/plugin-vue@^6.0.0":
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-6.0.0.tgz#3f8c3cdeb709d9646770eebead1babe6409bf059"
|
||||
integrity sha512-iAliE72WsdhjzTOp2DtvKThq1VBC4REhwRcaA+zPAAph6I+OQhUXv+Xu2KS7ElxYtb7Zc/3R30Hwv1DxEo7NXQ==
|
||||
dependencies:
|
||||
"@rolldown/pluginutils" "1.0.0-beta.19"
|
||||
|
||||
"@volar/language-core@2.4.14", "@volar/language-core@~2.4.11":
|
||||
version "2.4.14"
|
||||
@@ -3259,11 +3259,6 @@ unbox-primitive@^1.1.0:
|
||||
has-symbols "^1.1.0"
|
||||
which-boxed-primitive "^1.1.1"
|
||||
|
||||
undici-types@~6.21.0:
|
||||
version "6.21.0"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb"
|
||||
integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==
|
||||
|
||||
undici-types@~7.8.0:
|
||||
version "7.8.0"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.8.0.tgz#de00b85b710c54122e44fbfd911f8d70174cd294"
|
||||
@@ -3322,10 +3317,10 @@ update-browserslist-db@^1.1.3:
|
||||
escalade "^3.2.0"
|
||||
picocolors "^1.1.1"
|
||||
|
||||
vite-plugin-pwa@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vite-plugin-pwa/-/vite-plugin-pwa-1.0.0.tgz#bd0ee81e71851bd45cae2f4aec90e52e9833152d"
|
||||
integrity sha512-X77jo0AOd5OcxmWj3WnVti8n7Kw2tBgV1c8MCXFclrSlDV23ePzv2eTDIALXI2Qo6nJ5pZJeZAuX0AawvRfoeA==
|
||||
vite-plugin-pwa@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vite-plugin-pwa/-/vite-plugin-pwa-1.0.1.tgz#fec0cc65ffd592bb9ca8374176bd9a2aeaafe97b"
|
||||
integrity sha512-STyUomQbydj7vGamtgQYIJI0YsUZ3T4pJLGBQDQPhzMse6aGSncmEN21OV35PrFsmCvmtiH+Nu1JS1ke4RqBjQ==
|
||||
dependencies:
|
||||
debug "^4.3.6"
|
||||
pretty-bytes "^6.1.1"
|
||||
@@ -3368,13 +3363,13 @@ vue-draggable-plus@^0.6.0:
|
||||
dependencies:
|
||||
"@types/sortablejs" "^1.15.8"
|
||||
|
||||
vue-i18n@^11.1.3:
|
||||
version "11.1.6"
|
||||
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-11.1.6.tgz#7a9d4933da4a3fc5e4d6bd4b8c4c9a7d6c18af23"
|
||||
integrity sha512-+IbsW/sTZHj7U1w0rPOYJbuSB0/7DeO1nvUo3BxvO20OQgHs+ukJ3QeLqvoUA6DiLk+8SA9+djRmKC9+FC6cAg==
|
||||
vue-i18n@^11.1.7:
|
||||
version "11.1.7"
|
||||
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-11.1.7.tgz#a26c0224d1311ac89b82ff6d0ee45f68b5099237"
|
||||
integrity sha512-CDrU7Cmyh1AxJjerQmipV9nVa//exVBdhTcWGlbfcDCN8bKp/uAe7Le6IoN4//5emIikbsSKe9Uofmf/xXkhOA==
|
||||
dependencies:
|
||||
"@intlify/core-base" "11.1.6"
|
||||
"@intlify/shared" "11.1.6"
|
||||
"@intlify/core-base" "11.1.7"
|
||||
"@intlify/shared" "11.1.7"
|
||||
"@vue/devtools-api" "^6.5.0"
|
||||
|
||||
vue-router@^4.5.0:
|
||||
@@ -3417,10 +3412,10 @@ vuedraggable@^4.1.0:
|
||||
dependencies:
|
||||
sortablejs "1.14.0"
|
||||
|
||||
vuetify@^3.8.10:
|
||||
version "3.8.10"
|
||||
resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-3.8.10.tgz#73a33f900a9ad483e6c73b9b5a05851988e3e513"
|
||||
integrity sha512-3BETdCGh3eB1cV5+dA+L5CYi62Q/Jb09H512GImeYzMHd2R+LntO0F5pNCqVB4KoxymQ4Jej3Q0J6AYmf0KD8w==
|
||||
vuetify@^3.8.12:
|
||||
version "3.8.12"
|
||||
resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-3.8.12.tgz#7c433b8b036011bb0a800f08f5a53d61067eeae8"
|
||||
integrity sha512-XRX/yRel/V5rlas12ovujVCo8RDb/NwICyef/DVYzybqbYz/UGHZd23sN5q1zw0h9jUN8httXI6ytWN7OFugmA==
|
||||
|
||||
w3c-xmlserializer@^5.0.0:
|
||||
version "5.0.0"
|
||||
@@ -3521,7 +3516,7 @@ which-typed-array@^1.1.16, which-typed-array@^1.1.19:
|
||||
gopd "^1.2.0"
|
||||
has-tostringtag "^1.0.2"
|
||||
|
||||
workbox-background-sync@7.3.0:
|
||||
workbox-background-sync@7.3.0, workbox-background-sync@^7.3.0:
|
||||
version "7.3.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-7.3.0.tgz#b6340731a8d5b42b9e75a8a87c8806928e6e6303"
|
||||
integrity sha512-PCSk3eK7Mxeuyatb22pcSx9dlgWNv3+M8PqPaYDokks8Y5/FX4soaOqj3yhAZr5k6Q5JWTOMYgaJBpbw11G9Eg==
|
||||
@@ -3591,7 +3586,7 @@ workbox-core@7.3.0:
|
||||
resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-7.3.0.tgz#f24fb92041a0b7482fe2dd856544aaa9fa105248"
|
||||
integrity sha512-Z+mYrErfh4t3zi7NVTvOuACB0A/jA3bgxUN3PwtAVHvfEsZxV9Iju580VEETug3zYJRc0Dmii/aixI/Uxj8fmw==
|
||||
|
||||
workbox-expiration@7.3.0:
|
||||
workbox-expiration@7.3.0, workbox-expiration@^7.3.0:
|
||||
version "7.3.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-7.3.0.tgz#2c1ee1fdada34aa7e7474f706d5429c914bd10d2"
|
||||
integrity sha512-lpnSSLp2BM+K6bgFCWc5bS1LR5pAwDWbcKt1iL87/eTSJRdLdAwGQznZE+1czLgn/X05YChsrEegTNxjM067vQ==
|
||||
@@ -3609,14 +3604,14 @@ workbox-google-analytics@7.3.0:
|
||||
workbox-routing "7.3.0"
|
||||
workbox-strategies "7.3.0"
|
||||
|
||||
workbox-navigation-preload@7.3.0:
|
||||
workbox-navigation-preload@7.3.0, workbox-navigation-preload@^7.3.0:
|
||||
version "7.3.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-7.3.0.tgz#9d54693b9179d5175e66af5ef9a92d1b7cf3e605"
|
||||
integrity sha512-fTJzogmFaTv4bShZ6aA7Bfj4Cewaq5rp30qcxl2iYM45YD79rKIhvzNHiFj1P+u5ZZldroqhASXwwoyusnr2cg==
|
||||
dependencies:
|
||||
workbox-core "7.3.0"
|
||||
|
||||
workbox-precaching@7.3.0:
|
||||
workbox-precaching@7.3.0, workbox-precaching@^7.3.0:
|
||||
version "7.3.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-7.3.0.tgz#a84663d69efdb334f25c04dba0a72ed3391c4da8"
|
||||
integrity sha512-ckp/3t0msgXclVAYaNndAGeAoWQUv7Rwc4fdhWL69CCAb2UHo3Cef0KIUctqfQj1p8h6aGyz3w8Cy3Ihq9OmIw==
|
||||
@@ -3644,14 +3639,14 @@ workbox-recipes@7.3.0:
|
||||
workbox-routing "7.3.0"
|
||||
workbox-strategies "7.3.0"
|
||||
|
||||
workbox-routing@7.3.0:
|
||||
workbox-routing@7.3.0, workbox-routing@^7.3.0:
|
||||
version "7.3.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-7.3.0.tgz#fc86296bc1155c112ee2c16b3180853586c30208"
|
||||
integrity sha512-ZUlysUVn5ZUzMOmQN3bqu+gK98vNfgX/gSTZ127izJg/pMMy4LryAthnYtjuqcjkN4HEAx1mdgxNiKJMZQM76A==
|
||||
dependencies:
|
||||
workbox-core "7.3.0"
|
||||
|
||||
workbox-strategies@7.3.0:
|
||||
workbox-strategies@7.3.0, workbox-strategies@^7.3.0:
|
||||
version "7.3.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-7.3.0.tgz#bb1530f205806895aacdea3639e6cf6bfb3a6cb0"
|
||||
integrity sha512-tmZydug+qzDFATwX7QiEL5Hdf7FrkhjaF9db1CbB39sDmEZJg3l9ayDvPxy8Y18C3Y66Nrr9kkN1f/RlkDgllg==
|
||||
|
||||
Reference in New Issue
Block a user