chore: frontend testing setup (#1739)
* add vitest * initialize lib w/ tests * move to dev dep * run tests in CI * update file names * move api folder to lib * move api and api types to same folder * update generator outpath * rm husky * i guess i _did_ need those types * reorg types * extract validators into testable components * (WIP) start composable testing * fix import type * fix linter complaint * simplify icon type def * fix linter errors (maybe?) * rename client file for sorting
This commit is contained in:
parent
9f6bcc83d5
commit
fcc5d99d40
182 changed files with 902 additions and 487 deletions
4
.github/workflows/partial-frontend.yml
vendored
4
.github/workflows/partial-frontend.yml
vendored
|
@ -38,6 +38,10 @@ jobs:
|
||||||
run: yarn lint
|
run: yarn lint
|
||||||
working-directory: "frontend"
|
working-directory: "frontend"
|
||||||
|
|
||||||
|
- name: Run tests 🧪
|
||||||
|
run: yarn test:ci
|
||||||
|
working-directory: "frontend"
|
||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -55,7 +55,6 @@ develop-eggs/
|
||||||
downloads/
|
downloads/
|
||||||
eggs/
|
eggs/
|
||||||
.eggs/
|
.eggs/
|
||||||
lib/
|
|
||||||
lib64/
|
lib64/
|
||||||
parts/
|
parts/
|
||||||
!frontend/src/components/Recipe/Parts/
|
!frontend/src/components/Recipe/Parts/
|
||||||
|
|
|
@ -75,7 +75,7 @@ def generate_typescript_types() -> None:
|
||||||
return str_path
|
return str_path
|
||||||
|
|
||||||
schema_path = PROJECT_DIR / "mealie" / "schema"
|
schema_path = PROJECT_DIR / "mealie" / "schema"
|
||||||
types_dir = PROJECT_DIR / "frontend" / "types" / "api-types"
|
types_dir = PROJECT_DIR / "frontend" / "lib" / "api" / "types"
|
||||||
|
|
||||||
ignore_dirs = ["__pycache__", "static", "_mealie"]
|
ignore_dirs = ["__pycache__", "static", "_mealie"]
|
||||||
|
|
||||||
|
|
1
frontend/.husky/.gitignore
vendored
1
frontend/.husky/.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
_
|
|
|
@ -20,7 +20,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, useContext } from "@nuxtjs/composition-api";
|
import { defineComponent, useContext } from "@nuxtjs/composition-api";
|
||||||
import { parseISO, formatDistanceToNow } from "date-fns";
|
import { parseISO, formatDistanceToNow } from "date-fns";
|
||||||
import { GroupDataExport } from "~/types/api-types/group";
|
import { GroupDataExport } from "~/lib/api/types/group";
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
exports: {
|
exports: {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed, useContext } from "@nuxtjs/composition-api";
|
import { defineComponent, computed, useContext } from "@nuxtjs/composition-api";
|
||||||
import RecipeOrganizerSelector from "~/components/Domain/Recipe/RecipeOrganizerSelector.vue";
|
import RecipeOrganizerSelector from "~/components/Domain/Recipe/RecipeOrganizerSelector.vue";
|
||||||
import { RecipeTag, RecipeCategory } from "~/types/api-types/group";
|
import { RecipeTag, RecipeCategory } from "~/lib/api/types/group";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed, ref } from "@nuxtjs/composition-api";
|
import { defineComponent, computed, ref } from "@nuxtjs/composition-api";
|
||||||
import { ReadWebhook } from "~/types/api-types/group";
|
import { ReadWebhook } from "~/lib/api/types/group";
|
||||||
import { timeLocalToUTC, timeUTCToLocal } from "~/composables/use-group-webhooks";
|
import { timeLocalToUTC, timeUTCToLocal } from "~/composables/use-group-webhooks";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
|
|
@ -84,7 +84,7 @@
|
||||||
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api";
|
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api";
|
||||||
import RecipeContextMenu from "./RecipeContextMenu.vue";
|
import RecipeContextMenu from "./RecipeContextMenu.vue";
|
||||||
import RecipeFavoriteBadge from "./RecipeFavoriteBadge.vue";
|
import RecipeFavoriteBadge from "./RecipeFavoriteBadge.vue";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
const SAVE_EVENT = "save";
|
const SAVE_EVENT = "save";
|
||||||
const DELETE_EVENT = "delete";
|
const DELETE_EVENT = "delete";
|
||||||
|
|
|
@ -82,7 +82,7 @@ import { defineComponent, reactive, useContext } from "@nuxtjs/composition-api";
|
||||||
import { useStaticRoutes, useUserApi } from "~/composables/api";
|
import { useStaticRoutes, useUserApi } from "~/composables/api";
|
||||||
import { alert } from "~/composables/use-toast";
|
import { alert } from "~/composables/use-toast";
|
||||||
import { detectServerBaseUrl } from "~/composables/use-utils";
|
import { detectServerBaseUrl } from "~/composables/use-utils";
|
||||||
import { RecipeAsset } from "~/types/api-types/recipe";
|
import { RecipeAsset } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -129,7 +129,7 @@ import RecipeCard from "./RecipeCard.vue";
|
||||||
import RecipeCardMobile from "./RecipeCardMobile.vue";
|
import RecipeCardMobile from "./RecipeCardMobile.vue";
|
||||||
import { useAsyncKey } from "~/composables/use-utils";
|
import { useAsyncKey } from "~/composables/use-utils";
|
||||||
import { useLazyRecipes } from "~/composables/recipes";
|
import { useLazyRecipes } from "~/composables/recipes";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
import { useUserSortPreferences } from "~/composables/use-users/preferences";
|
import { useUserSortPreferences } from "~/composables/use-users/preferences";
|
||||||
|
|
||||||
const REPLACE_RECIPES_EVENT = "replaceRecipes";
|
const REPLACE_RECIPES_EVENT = "replaceRecipes";
|
||||||
|
@ -230,13 +230,13 @@ export default defineComponent({
|
||||||
page.value,
|
page.value,
|
||||||
|
|
||||||
// we double-up the first call to avoid a bug with large screens that render the entire first page without scrolling, preventing additional loading
|
// we double-up the first call to avoid a bug with large screens that render the entire first page without scrolling, preventing additional loading
|
||||||
perPage.value*2,
|
perPage.value * 2,
|
||||||
preferences.value.orderBy,
|
preferences.value.orderBy,
|
||||||
preferences.value.orderDirection,
|
preferences.value.orderDirection,
|
||||||
cookbook.value,
|
cookbook.value,
|
||||||
category.value,
|
category.value,
|
||||||
tag.value,
|
tag.value,
|
||||||
tool.value,
|
tool.value
|
||||||
);
|
);
|
||||||
|
|
||||||
// since we doubled the first call, we also need to advance the page
|
// since we doubled the first call, we also need to advance the page
|
||||||
|
@ -263,7 +263,7 @@ export default defineComponent({
|
||||||
cookbook.value,
|
cookbook.value,
|
||||||
category.value,
|
category.value,
|
||||||
tag.value,
|
tag.value,
|
||||||
tool.value,
|
tool.value
|
||||||
);
|
);
|
||||||
if (!newRecipes.length) {
|
if (!newRecipes.length) {
|
||||||
hasMore.value = false;
|
hasMore.value = false;
|
||||||
|
@ -325,7 +325,7 @@ export default defineComponent({
|
||||||
cookbook.value,
|
cookbook.value,
|
||||||
category.value,
|
category.value,
|
||||||
tag.value,
|
tag.value,
|
||||||
tool.value,
|
tool.value
|
||||||
);
|
);
|
||||||
context.emit(REPLACE_RECIPES_EVENT, newRecipes);
|
context.emit(REPLACE_RECIPES_EVENT, newRecipes);
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "@nuxtjs/composition-api";
|
import { defineComponent } from "@nuxtjs/composition-api";
|
||||||
import { RecipeCategory, RecipeTag, RecipeTool } from "~/types/api-types/user";
|
import { RecipeCategory, RecipeTag, RecipeTool } from "~/lib/api/types/user";
|
||||||
|
|
||||||
export type UrlPrefixParam = "tags" | "categories" | "tools";
|
export type UrlPrefixParam = "tags" | "categories" | "tools";
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref, toRefs, onMounted, reactive } from "@nuxtjs/composition-api";
|
import { defineComponent, ref, toRefs, onMounted, reactive } from "@nuxtjs/composition-api";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { RecipeCommentOut } from "~/types/api-types/recipe";
|
import { RecipeCommentOut } from "~/lib/api/types/recipe";
|
||||||
import UserAvatar from "~/components/Domain/User/UserAvatar.vue";
|
import UserAvatar from "~/components/Domain/User/UserAvatar.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
|
|
@ -101,8 +101,8 @@ import RecipeDialogShare from "./RecipeDialogShare.vue";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { alert } from "~/composables/use-toast";
|
import { alert } from "~/composables/use-toast";
|
||||||
import { planTypeOptions } from "~/composables/use-group-mealplan";
|
import { planTypeOptions } from "~/composables/use-group-mealplan";
|
||||||
import { ShoppingListSummary } from "~/types/api-types/group";
|
import { ShoppingListSummary } from "~/lib/api/types/group";
|
||||||
import { PlanEntryType } from "~/types/api-types/meal-plan";
|
import { PlanEntryType } from "~/lib/api/types/meal-plan";
|
||||||
import { useAxiosDownloader } from "~/composables/api/use-axios-download";
|
import { useAxiosDownloader } from "~/composables/api/use-axios-download";
|
||||||
import { useCopy } from "~/composables/use-copy";
|
import { useCopy } from "~/composables/use-copy";
|
||||||
|
|
||||||
|
|
|
@ -44,9 +44,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, onMounted, ref, useContext } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, onMounted, ref, useContext } from "@nuxtjs/composition-api";
|
||||||
import RecipeChip from "./RecipeChips.vue";
|
import RecipeChip from "./RecipeChips.vue";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { UserOut } from "~/types/api-types/user";
|
import { UserOut } from "~/lib/api/types/user";
|
||||||
|
|
||||||
const INPUT_EVENT = "input";
|
const INPUT_EVENT = "input";
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
import { defineComponent, toRefs, reactive, ref, watch, useRoute } from "@nuxtjs/composition-api";
|
import { defineComponent, toRefs, reactive, ref, watch, useRoute } from "@nuxtjs/composition-api";
|
||||||
import RecipeCardMobile from "./RecipeCardMobile.vue";
|
import RecipeCardMobile from "./RecipeCardMobile.vue";
|
||||||
import { useRecipes, allRecipes, useRecipeSearch } from "~/composables/recipes";
|
import { useRecipes, allRecipes, useRecipeSearch } from "~/composables/recipes";
|
||||||
import { RecipeSummary } from "~/types/api-types/recipe";
|
import { RecipeSummary } from "~/lib/api/types/recipe";
|
||||||
const SELECTED_EVENT = "selected";
|
const SELECTED_EVENT = "selected";
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -58,7 +58,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed, toRefs, reactive, useContext } from "@nuxtjs/composition-api";
|
import { defineComponent, computed, toRefs, reactive, useContext } from "@nuxtjs/composition-api";
|
||||||
import { useClipboard, useShare, whenever } from "@vueuse/core";
|
import { useClipboard, useShare, whenever } from "@vueuse/core";
|
||||||
import { RecipeShareToken } from "~/types/api-types/recipe";
|
import { RecipeShareToken } from "~/lib/api/types/recipe";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { alert } from "~/composables/use-toast";
|
import { alert } from "~/composables/use-toast";
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { UserOut } from "~/types/api-types/user";
|
import { UserOut } from "~/lib/api/types/user";
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
slug: {
|
slug: {
|
||||||
|
|
|
@ -131,7 +131,7 @@
|
||||||
import { computed, defineComponent, reactive, ref, toRefs, useContext } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, reactive, ref, toRefs, useContext } from "@nuxtjs/composition-api";
|
||||||
import { useFoodStore, useFoodData, useUnitStore, useUnitData } from "~/composables/store";
|
import { useFoodStore, useFoodData, useUnitStore, useUnitData } from "~/composables/store";
|
||||||
import { validators } from "~/composables/use-validators";
|
import { validators } from "~/composables/use-validators";
|
||||||
import { RecipeIngredient } from "~/types/api-types/recipe";
|
import { RecipeIngredient } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
import { computed, defineComponent, reactive, toRefs } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, reactive, toRefs } from "@nuxtjs/composition-api";
|
||||||
// @ts-ignore vue-markdown has no types
|
// @ts-ignore vue-markdown has no types
|
||||||
import { parseIngredientText } from "~/composables/recipes";
|
import { parseIngredientText } from "~/composables/recipes";
|
||||||
import { RecipeIngredient } from "~/types/api-types/recipe";
|
import { RecipeIngredient } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {},
|
components: {},
|
||||||
|
|
|
@ -230,7 +230,7 @@ import {
|
||||||
useContext,
|
useContext,
|
||||||
computed,
|
computed,
|
||||||
} from "@nuxtjs/composition-api";
|
} from "@nuxtjs/composition-api";
|
||||||
import { RecipeStep, IngredientReferences, RecipeIngredient, RecipeAsset } from "~/types/api-types/recipe";
|
import { RecipeStep, IngredientReferences, RecipeIngredient, RecipeAsset } from "~/lib/api/types/recipe";
|
||||||
import { parseIngredientText } from "~/composables/recipes";
|
import { parseIngredientText } from "~/composables/recipes";
|
||||||
import { uuid4, detectServerBaseUrl } from "~/composables/use-utils";
|
import { uuid4, detectServerBaseUrl } from "~/composables/use-utils";
|
||||||
import { useUserApi, useStaticRoutes } from "~/composables/api";
|
import { useUserApi, useStaticRoutes } from "~/composables/api";
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "@nuxtjs/composition-api";
|
import { defineComponent } from "@nuxtjs/composition-api";
|
||||||
import { RecipeSummary } from "~/types/api-types/recipe";
|
import { RecipeSummary } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "@nuxtjs/composition-api";
|
import { defineComponent } from "@nuxtjs/composition-api";
|
||||||
import { RecipeNote } from "~/types/api-types/recipe";
|
import { RecipeNote } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
||||||
import { Nutrition } from "~/types/api-types/recipe";
|
import { Nutrition } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -146,9 +146,9 @@ import { until } from "@vueuse/core";
|
||||||
import { invoke } from "@vueuse/shared";
|
import { invoke } from "@vueuse/shared";
|
||||||
import draggable from "vuedraggable";
|
import draggable from "vuedraggable";
|
||||||
import { useUserApi, useStaticRoutes } from "~/composables/api";
|
import { useUserApi, useStaticRoutes } from "~/composables/api";
|
||||||
import { OcrTsvResponse as NullableOcrTsvResponse } from "~/types/api-types/ocr";
|
import { OcrTsvResponse as NullableOcrTsvResponse } from "~/lib/api/types/ocr";
|
||||||
import { validators } from "~/composables/use-validators";
|
import { validators } from "~/composables/use-validators";
|
||||||
import { Recipe, RecipeIngredient, RecipeStep } from "~/types/api-types/recipe";
|
import { Recipe, RecipeIngredient, RecipeStep } from "~/lib/api/types/recipe";
|
||||||
import { Paths, Leaves, SelectedRecipeLeaves } from "~/types/ocr-types";
|
import { Paths, Leaves, SelectedRecipeLeaves } from "~/types/ocr-types";
|
||||||
import BannerExperimental from "~/components/global/BannerExperimental.vue";
|
import BannerExperimental from "~/components/global/BannerExperimental.vue";
|
||||||
import RecipeDialogBulkAdd from "~/components/Domain/Recipe/RecipeDialogBulkAdd.vue";
|
import RecipeDialogBulkAdd from "~/components/Domain/Recipe/RecipeDialogBulkAdd.vue";
|
||||||
|
@ -157,7 +157,7 @@ import RecipeIngredientEditor from "~/components/Domain/Recipe/RecipeIngredientE
|
||||||
import RecipeOcrEditorPageCanvas from "~/components/Domain/Recipe/RecipeOcrEditorPage/RecipeOcrEditorPageParts/RecipeOcrEditorPageCanvas.vue";
|
import RecipeOcrEditorPageCanvas from "~/components/Domain/Recipe/RecipeOcrEditorPage/RecipeOcrEditorPageParts/RecipeOcrEditorPageCanvas.vue";
|
||||||
import RecipeOcrEditorPageHelp from "~/components/Domain/Recipe/RecipeOcrEditorPage/RecipeOcrEditorPageParts/RecipeOcrEditorPageHelp.vue";
|
import RecipeOcrEditorPageHelp from "~/components/Domain/Recipe/RecipeOcrEditorPage/RecipeOcrEditorPageParts/RecipeOcrEditorPageHelp.vue";
|
||||||
import { uuid4 } from "~/composables/use-utils";
|
import { uuid4 } from "~/composables/use-utils";
|
||||||
import { NoUndefinedField } from "~/types/api";
|
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||||
|
|
||||||
// Temporary Shim until we have a better solution
|
// Temporary Shim until we have a better solution
|
||||||
// https://github.com/phillipdupuis/pydantic-to-typescript/issues/28
|
// https://github.com/phillipdupuis/pydantic-to-typescript/issues/28
|
||||||
|
|
|
@ -41,8 +41,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive, useContext, ref, toRefs, watch } from "@nuxtjs/composition-api";
|
import { defineComponent, reactive, useContext, ref, toRefs, watch } from "@nuxtjs/composition-api";
|
||||||
import { onMounted } from "vue-demi";
|
import { onMounted } from "vue-demi";
|
||||||
import { NoUndefinedField } from "~/types/api";
|
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||||
import { OcrTsvResponse as NullableOcrTsvResponse } from "~/types/api-types/ocr";
|
import { OcrTsvResponse as NullableOcrTsvResponse } from "~/lib/api/types/ocr";
|
||||||
import { CanvasModes, SelectedTextSplitModes, ImagePosition, Mouse, CanvasRect, ToolbarIcons } from "~/types/ocr-types";
|
import { CanvasModes, SelectedTextSplitModes, ImagePosition, Mouse, CanvasRect, ToolbarIcons } from "~/types/ocr-types";
|
||||||
|
|
||||||
// Temporary Shim until we have a better solution
|
// Temporary Shim until we have a better solution
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
import { computed, defineComponent, reactive, toRefs, useContext, watch } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, reactive, toRefs, useContext, watch } from "@nuxtjs/composition-api";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { useCategoryStore, useTagStore, useToolStore } from "~/composables/store";
|
import { useCategoryStore, useTagStore, useToolStore } from "~/composables/store";
|
||||||
import { RecipeOrganizer, Organizer } from "~/types/recipe/organizers";
|
import { RecipeOrganizer, Organizer } from "~/lib/api/types/non-generated";
|
||||||
|
|
||||||
const CREATED_ITEM_EVENT = "created-item";
|
const CREATED_ITEM_EVENT = "created-item";
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
import { defineComponent, computed, ref } from "@nuxtjs/composition-api";
|
import { defineComponent, computed, ref } from "@nuxtjs/composition-api";
|
||||||
import { useContextPresets } from "~/composables/use-context-presents";
|
import { useContextPresets } from "~/composables/use-context-presents";
|
||||||
import RecipeOrganizerDialog from "~/components/Domain/Recipe/RecipeOrganizerDialog.vue";
|
import RecipeOrganizerDialog from "~/components/Domain/Recipe/RecipeOrganizerDialog.vue";
|
||||||
import { RecipeOrganizer } from "~/types/recipe/organizers";
|
import { RecipeOrganizer } from "~/lib/api/types/non-generated";
|
||||||
|
|
||||||
interface GenericItem {
|
interface GenericItem {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
|
|
@ -42,11 +42,11 @@
|
||||||
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api";
|
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api";
|
||||||
import { computed, onMounted } from "vue-demi";
|
import { computed, onMounted } from "vue-demi";
|
||||||
import RecipeOrganizerDialog from "./RecipeOrganizerDialog.vue";
|
import RecipeOrganizerDialog from "./RecipeOrganizerDialog.vue";
|
||||||
import { RecipeCategory, RecipeTag } from "~/types/api-types/user";
|
import { RecipeCategory, RecipeTag } from "~/lib/api/types/user";
|
||||||
import { RecipeTool } from "~/types/api-types/admin";
|
import { RecipeTool } from "~/lib/api/types/admin";
|
||||||
import { useTagStore } from "~/composables/store/use-tag-store";
|
import { useTagStore } from "~/composables/store/use-tag-store";
|
||||||
import { useCategoryStore, useToolStore } from "~/composables/store";
|
import { useCategoryStore, useToolStore } from "~/composables/store";
|
||||||
import { Organizer, RecipeOrganizer } from "~/types/recipe/organizers";
|
import { Organizer, RecipeOrganizer } from "~/lib/api/types/non-generated";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -90,8 +90,8 @@ import RecipePageTitleContent from "./RecipePageParts/RecipePageTitleContent.vue
|
||||||
import RecipePageComments from "./RecipePageParts/RecipePageComments.vue";
|
import RecipePageComments from "./RecipePageParts/RecipePageComments.vue";
|
||||||
import RecipePrintView from "~/components/Domain/Recipe/RecipePrintView.vue";
|
import RecipePrintView from "~/components/Domain/Recipe/RecipePrintView.vue";
|
||||||
import { EditorMode, PageMode, usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
import { EditorMode, PageMode, usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
||||||
import { NoUndefinedField } from "~/types/api";
|
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
import { useRecipeMeta } from "~/composables/recipes";
|
import { useRecipeMeta } from "~/composables/recipes";
|
||||||
import { useRouteQuery } from "~/composables/use-router";
|
import { useRouteQuery } from "~/composables/use-router";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
|
|
|
@ -56,9 +56,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref, toRefs, onMounted, reactive } from "@nuxtjs/composition-api";
|
import { defineComponent, ref, toRefs, onMounted, reactive } from "@nuxtjs/composition-api";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { Recipe, RecipeCommentOut } from "~/types/api-types/recipe";
|
import { Recipe, RecipeCommentOut } from "~/lib/api/types/recipe";
|
||||||
import UserAvatar from "~/components/Domain/User/UserAvatar.vue";
|
import UserAvatar from "~/components/Domain/User/UserAvatar.vue";
|
||||||
import { NoUndefinedField } from "~/types/api";
|
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||||
import { usePageUser } from "~/composables/recipe-page/shared-state";
|
import { usePageUser } from "~/composables/recipe-page/shared-state";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, onUnmounted } from "@nuxtjs/composition-api";
|
import { defineComponent, onUnmounted } from "@nuxtjs/composition-api";
|
||||||
import { clearPageState, usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
import { clearPageState, usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
||||||
import { NoUndefinedField } from "~/types/api";
|
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import RecipeImageUploadBtn from "~/components/Domain/Recipe/RecipeImageUploadBtn.vue";
|
import RecipeImageUploadBtn from "~/components/Domain/Recipe/RecipeImageUploadBtn.vue";
|
||||||
import RecipeSettingsMenu from "~/components/Domain/Recipe/RecipeSettingsMenu.vue";
|
import RecipeSettingsMenu from "~/components/Domain/Recipe/RecipeSettingsMenu.vue";
|
||||||
|
|
|
@ -57,8 +57,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref } from "@nuxtjs/composition-api";
|
import { defineComponent, ref } from "@nuxtjs/composition-api";
|
||||||
import { usePageState } from "~/composables/recipe-page/shared-state";
|
import { usePageState } from "~/composables/recipe-page/shared-state";
|
||||||
import { NoUndefinedField } from "~/types/api";
|
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
recipe: {
|
recipe: {
|
||||||
|
|
|
@ -61,8 +61,8 @@ import RecipeRating from "~/components/Domain/Recipe/RecipeRating.vue";
|
||||||
import RecipeActionMenu from "~/components/Domain/Recipe/RecipeActionMenu.vue";
|
import RecipeActionMenu from "~/components/Domain/Recipe/RecipeActionMenu.vue";
|
||||||
import RecipeTimeCard from "~/components/Domain/Recipe/RecipeTimeCard.vue";
|
import RecipeTimeCard from "~/components/Domain/Recipe/RecipeTimeCard.vue";
|
||||||
import { useStaticRoutes } from "~/composables/api";
|
import { useStaticRoutes } from "~/composables/api";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
import { NoUndefinedField } from "~/types/api";
|
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||||
import { usePageState, usePageUser, PageMode, EditorMode } from "~/composables/recipe-page/shared-state";
|
import { usePageState, usePageUser, PageMode, EditorMode } from "~/composables/recipe-page/shared-state";
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -55,8 +55,8 @@
|
||||||
import draggable from "vuedraggable";
|
import draggable from "vuedraggable";
|
||||||
import { computed, defineComponent, ref } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, ref } from "@nuxtjs/composition-api";
|
||||||
import { usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
import { usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
||||||
import { NoUndefinedField } from "~/types/api";
|
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
import RecipeIngredientEditor from "~/components/Domain/Recipe/RecipeIngredientEditor.vue";
|
import RecipeIngredientEditor from "~/components/Domain/Recipe/RecipeIngredientEditor.vue";
|
||||||
import RecipeDialogBulkAdd from "~/components/Domain/Recipe/RecipeDialogBulkAdd.vue";
|
import RecipeDialogBulkAdd from "~/components/Domain/Recipe/RecipeDialogBulkAdd.vue";
|
||||||
import { uuid4 } from "~/composables/use-utils";
|
import { uuid4 } from "~/composables/use-utils";
|
||||||
|
|
|
@ -28,8 +28,8 @@
|
||||||
import { defineComponent } from "@nuxtjs/composition-api";
|
import { defineComponent } from "@nuxtjs/composition-api";
|
||||||
import { usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
import { usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
||||||
import { useToolStore } from "~/composables/store";
|
import { useToolStore } from "~/composables/store";
|
||||||
import { NoUndefinedField } from "~/types/api";
|
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
import RecipeIngredients from "~/components/Domain/Recipe/RecipeIngredients.vue";
|
import RecipeIngredients from "~/components/Domain/Recipe/RecipeIngredients.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
|
|
@ -225,12 +225,12 @@ import {
|
||||||
computed,
|
computed,
|
||||||
} from "@nuxtjs/composition-api";
|
} from "@nuxtjs/composition-api";
|
||||||
import RecipeIngredientHtml from "../../RecipeIngredientHtml.vue";
|
import RecipeIngredientHtml from "../../RecipeIngredientHtml.vue";
|
||||||
import { RecipeStep, IngredientReferences, RecipeIngredient, RecipeAsset, Recipe } from "~/types/api-types/recipe";
|
import { RecipeStep, IngredientReferences, RecipeIngredient, RecipeAsset, Recipe } from "~/lib/api/types/recipe";
|
||||||
import { parseIngredientText } from "~/composables/recipes";
|
import { parseIngredientText } from "~/composables/recipes";
|
||||||
import { uuid4, detectServerBaseUrl } from "~/composables/use-utils";
|
import { uuid4, detectServerBaseUrl } from "~/composables/use-utils";
|
||||||
import { useUserApi, useStaticRoutes } from "~/composables/api";
|
import { useUserApi, useStaticRoutes } from "~/composables/api";
|
||||||
import { usePageState } from "~/composables/recipe-page/shared-state";
|
import { usePageState } from "~/composables/recipe-page/shared-state";
|
||||||
import { NoUndefinedField } from "~/types/api";
|
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||||
import DropZone from "~/components/global/DropZone.vue";
|
import DropZone from "~/components/global/DropZone.vue";
|
||||||
|
|
||||||
interface MergerHistory {
|
interface MergerHistory {
|
||||||
|
|
|
@ -59,8 +59,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "@nuxtjs/composition-api";
|
import { defineComponent } from "@nuxtjs/composition-api";
|
||||||
import { usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
import { usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
||||||
import { NoUndefinedField } from "~/types/api";
|
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
import RecipeOrganizerSelector from "@/components/Domain/Recipe/RecipeOrganizerSelector.vue";
|
import RecipeOrganizerSelector from "@/components/Domain/Recipe/RecipeOrganizerSelector.vue";
|
||||||
import RecipeNutrition from "~/components/Domain/Recipe/RecipeNutrition.vue";
|
import RecipeNutrition from "~/components/Domain/Recipe/RecipeNutrition.vue";
|
||||||
import RecipeChips from "@/components/Domain/Recipe/RecipeChips.vue";
|
import RecipeChips from "@/components/Domain/Recipe/RecipeChips.vue";
|
||||||
|
|
|
@ -30,8 +30,8 @@
|
||||||
import { computed, defineComponent } from "@nuxtjs/composition-api";
|
import { computed, defineComponent } from "@nuxtjs/composition-api";
|
||||||
import RecipeScaleEditButton from "~/components/Domain/Recipe/RecipeScaleEditButton.vue";
|
import RecipeScaleEditButton from "~/components/Domain/Recipe/RecipeScaleEditButton.vue";
|
||||||
import RecipeRating from "~/components/Domain/Recipe/RecipeRating.vue";
|
import RecipeRating from "~/components/Domain/Recipe/RecipeRating.vue";
|
||||||
import { NoUndefinedField } from "~/types/api";
|
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
import { usePageState } from "~/composables/recipe-page/shared-state";
|
import { usePageState } from "~/composables/recipe-page/shared-state";
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -44,8 +44,8 @@
|
||||||
import { defineComponent } from "@nuxtjs/composition-api";
|
import { defineComponent } from "@nuxtjs/composition-api";
|
||||||
import { usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
import { usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
||||||
import { validators } from "~/composables/use-validators";
|
import { validators } from "~/composables/use-validators";
|
||||||
import { NoUndefinedField } from "~/types/api";
|
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
import RecipeRating from "~/components/Domain/Recipe/RecipeRating.vue";
|
import RecipeRating from "~/components/Domain/Recipe/RecipeRating.vue";
|
||||||
import RecipeTimeCard from "~/components/Domain/Recipe/RecipeTimeCard.vue";
|
import RecipeTimeCard from "~/components/Domain/Recipe/RecipeTimeCard.vue";
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,12 @@
|
||||||
class="print-section"
|
class="print-section"
|
||||||
>
|
>
|
||||||
<h4 v-if="ingredientSection.ingredients[0].title" class="ingredient-title mt-2">
|
<h4 v-if="ingredientSection.ingredients[0].title" class="ingredient-title mt-2">
|
||||||
{{ ingredientSection.ingredients[0].title }}
|
{{ ingredientSection.ingredients[0].title }}
|
||||||
</h4>
|
</h4>
|
||||||
<div class="ingredient-grid" :style="{gridTemplateRows:`repeat(${Math.ceil(ingredientSection.ingredients.length / 2)}, min-content)`}">
|
<div
|
||||||
|
class="ingredient-grid"
|
||||||
|
:style="{ gridTemplateRows: `repeat(${Math.ceil(ingredientSection.ingredients.length / 2)}, min-content)` }"
|
||||||
|
>
|
||||||
<template v-for="(ingredient, ingredientIndex) in ingredientSection.ingredients">
|
<template v-for="(ingredient, ingredientIndex) in ingredientSection.ingredients">
|
||||||
<p :key="`ingredient-${ingredientIndex}`" class="ingredient-body" v-html="parseText(ingredient)" />
|
<p :key="`ingredient-${ingredientIndex}`" class="ingredient-body" v-html="parseText(ingredient)" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -70,7 +73,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed } from "@nuxtjs/composition-api";
|
import { defineComponent, computed } from "@nuxtjs/composition-api";
|
||||||
import RecipeTimeCard from "~/components/Domain/Recipe/RecipeTimeCard.vue";
|
import RecipeTimeCard from "~/components/Domain/Recipe/RecipeTimeCard.vue";
|
||||||
import { Recipe, RecipeIngredient, RecipeStep } from "~/types/api-types/recipe";
|
import { Recipe, RecipeIngredient, RecipeStep } from "~/lib/api/types/recipe";
|
||||||
import { parseIngredientText } from "~/composables/recipes";
|
import { parseIngredientText } from "~/composables/recipes";
|
||||||
|
|
||||||
type IngredientSection = {
|
type IngredientSection = {
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, useContext } from "@nuxtjs/composition-api";
|
import { defineComponent, useContext } from "@nuxtjs/composition-api";
|
||||||
import { RecipeSettings } from "~/types/api-types/recipe";
|
import { RecipeSettings } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref, computed } from "@nuxtjs/composition-api";
|
import { defineComponent, ref, computed } from "@nuxtjs/composition-api";
|
||||||
import { RecipeTool } from "~/types/api-types/recipe";
|
import { RecipeTool } from "~/lib/api/types/recipe";
|
||||||
import { useTools } from "~/composables/recipes";
|
import { useTools } from "~/composables/recipes";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent } from "@nuxtjs/composition-api";
|
import { computed, defineComponent } from "@nuxtjs/composition-api";
|
||||||
import { MultiPurposeLabelSummary } from "~/types/api-types/recipe";
|
import { MultiPurposeLabelSummary } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -55,9 +55,9 @@
|
||||||
import { defineComponent, computed, ref, useContext } from "@nuxtjs/composition-api";
|
import { defineComponent, computed, ref, useContext } from "@nuxtjs/composition-api";
|
||||||
import ShoppingListItemEditor from "./ShoppingListItemEditor.vue";
|
import ShoppingListItemEditor from "./ShoppingListItemEditor.vue";
|
||||||
import MultiPurposeLabel from "./MultiPurposeLabel.vue";
|
import MultiPurposeLabel from "./MultiPurposeLabel.vue";
|
||||||
import { ShoppingListItemCreate } from "~/types/api-types/group";
|
import { ShoppingListItemCreate } from "~/lib/api/types/group";
|
||||||
import { MultiPurposeLabelOut } from "~/types/api-types/labels";
|
import { MultiPurposeLabelOut } from "~/lib/api/types/labels";
|
||||||
import { IngredientFood, IngredientUnit } from "~/types/api-types/recipe";
|
import { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
|
||||||
import { getDisplayText } from "~/composables/use-display-text";
|
import { getDisplayText } from "~/composables/use-display-text";
|
||||||
|
|
||||||
interface actions {
|
interface actions {
|
||||||
|
|
|
@ -96,9 +96,9 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed } from "@nuxtjs/composition-api";
|
import { defineComponent, computed } from "@nuxtjs/composition-api";
|
||||||
import { ShoppingListItemCreate, ShoppingListItemOut } from "~/types/api-types/group";
|
import { ShoppingListItemCreate, ShoppingListItemOut } from "~/lib/api/types/group";
|
||||||
import { MultiPurposeLabelOut } from "~/types/api-types/labels";
|
import { MultiPurposeLabelOut } from "~/lib/api/types/labels";
|
||||||
import { IngredientFood, IngredientUnit } from "~/types/api-types/recipe";
|
import { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, toRefs, reactive, useContext, computed } from "@nuxtjs/composition-api";
|
import { defineComponent, toRefs, reactive, useContext, computed } from "@nuxtjs/composition-api";
|
||||||
import { UserOut } from "~/types/api-types/user";
|
import { UserOut } from "~/lib/api/types/user";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
@ -35,7 +35,7 @@ export default defineComponent({
|
||||||
|
|
||||||
const imageURL = computed(() => {
|
const imageURL = computed(() => {
|
||||||
// TODO Setup correct user type for $auth.user
|
// TODO Setup correct user type for $auth.user
|
||||||
const user = $auth.user as unknown as (UserOut | null);
|
const user = $auth.user as unknown as UserOut | null;
|
||||||
const key = user?.cacheKey ?? "";
|
const key = user?.cacheKey ?? "";
|
||||||
return `/api/media/users/${props.userId}/profile.webp?cacheKey=${key}`;
|
return `/api/media/users/${props.userId}/profile.webp?cacheKey=${key}`;
|
||||||
});
|
});
|
||||||
|
|
|
@ -32,8 +32,8 @@
|
||||||
* using the .sync syntax `item-id.sync="item.labelId"`
|
* using the .sync syntax `item-id.sync="item.labelId"`
|
||||||
*/
|
*/
|
||||||
import { defineComponent, computed } from "@nuxtjs/composition-api";
|
import { defineComponent, computed } from "@nuxtjs/composition-api";
|
||||||
import { MultiPurposeLabelSummary } from "~/types/api-types/labels";
|
import { MultiPurposeLabelSummary } from "~/lib/api/types/labels";
|
||||||
import { IngredientFood, IngredientUnit } from "~/types/api-types/recipe";
|
import { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -27,4 +27,3 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, useContext, useRouter } from "@nuxtjs/composition-api";
|
import { defineComponent, useContext, useRouter } from "@nuxtjs/composition-api";
|
||||||
import { ReportSummary } from "~/types/api-types/reports";
|
import { ReportSummary } from "~/lib/api/types/reports";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import { useContext } from "@nuxtjs/composition-api";
|
import { useContext } from "@nuxtjs/composition-api";
|
||||||
import type { NuxtAxiosInstance } from "@nuxtjs/axios";
|
import type { NuxtAxiosInstance } from "@nuxtjs/axios";
|
||||||
import { AdminAPI, Api } from "~/api";
|
import { ApiRequestInstance, RequestResponse } from "~/lib/api/types/non-generated";
|
||||||
import { ApiRequestInstance, RequestResponse } from "~/types/api";
|
import { AdminAPI, PublicApi, UserApi } from "~/lib/api";
|
||||||
import { PublicApi } from "~/api/public-api";
|
|
||||||
|
|
||||||
const request = {
|
const request = {
|
||||||
async safe<T, U>(
|
async safe<T, U>(
|
||||||
|
@ -66,12 +65,12 @@ export const useAdminApi = function (): AdminAPI {
|
||||||
return new AdminAPI(requests);
|
return new AdminAPI(requests);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useUserApi = function (): Api {
|
export const useUserApi = function (): UserApi {
|
||||||
const { $axios, i18n } = useContext();
|
const { $axios, i18n } = useContext();
|
||||||
$axios.setHeader("Accept-Language", i18n.locale);
|
$axios.setHeader("Accept-Language", i18n.locale);
|
||||||
|
|
||||||
const requests = getRequests($axios);
|
const requests = getRequests($axios);
|
||||||
return new Api(requests);
|
return new UserApi(requests);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const usePublicApi = function (): PublicApi {
|
export const usePublicApi = function (): PublicApi {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ref, Ref, useAsync, useContext } from "@nuxtjs/composition-api";
|
import { ref, Ref, useAsync, useContext } from "@nuxtjs/composition-api";
|
||||||
import { useAsyncKey } from "../use-utils";
|
import { useAsyncKey } from "../use-utils";
|
||||||
import { AppInfo } from "~/types/api-types/admin";
|
import { AppInfo } from "~/lib/api/types/admin";
|
||||||
|
|
||||||
export function useAppInfo(): Ref<AppInfo | null> {
|
export function useAppInfo(): Ref<AppInfo | null> {
|
||||||
const appInfo = ref<null | AppInfo>(null);
|
const appInfo = ref<null | AppInfo>(null);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Ref, useAsync } from "@nuxtjs/composition-api";
|
import { Ref, useAsync } from "@nuxtjs/composition-api";
|
||||||
import { useAsyncKey } from "../use-utils";
|
import { useAsyncKey } from "../use-utils";
|
||||||
import { BaseCRUDAPI } from "~/api/_base";
|
import { BaseCRUDAPI } from "~/lib/api/base/base-clients";
|
||||||
|
|
||||||
type BoundT = {
|
type BoundT = {
|
||||||
id?: string | number;
|
id?: string | number;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { computed, ComputedRef, ref, Ref, useContext } from "@nuxtjs/composition-api";
|
import { computed, ComputedRef, ref, Ref, useContext } from "@nuxtjs/composition-api";
|
||||||
import { UserOut } from "~/types/api-types/user";
|
import { UserOut } from "~/lib/api/types/user";
|
||||||
|
|
||||||
export enum PageMode {
|
export enum PageMode {
|
||||||
EDIT = "EDIT",
|
EDIT = "EDIT",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import DOMPurify from "isomorphic-dompurify";
|
import DOMPurify from "isomorphic-dompurify";
|
||||||
import { useFraction } from "./use-fraction";
|
import { useFraction } from "./use-fraction";
|
||||||
import { RecipeIngredient } from "~/types/api-types/recipe";
|
import { RecipeIngredient } from "~/lib/api/types/recipe";
|
||||||
const { frac } = useFraction();
|
const { frac } = useFraction();
|
||||||
|
|
||||||
function sanitizeIngredientHTML(rawHtml: string) {
|
function sanitizeIngredientHTML(rawHtml: string) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Ref } from "@nuxtjs/composition-api";
|
import { Ref } from "@nuxtjs/composition-api";
|
||||||
import { useStaticRoutes } from "~/composables/api";
|
import { useStaticRoutes } from "~/composables/api";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export interface RecipeMeta {
|
export interface RecipeMeta {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
@ -53,6 +53,6 @@ export const useRecipeMeta = () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
return { recipeMeta };
|
return { recipeMeta };
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { computed, reactive, ref, Ref } from "@nuxtjs/composition-api";
|
import { computed, reactive, ref, Ref } from "@nuxtjs/composition-api";
|
||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export const useRecipeSearch = (recipes: Ref<Recipe[] | null>) => {
|
export const useRecipeSearch = (recipes: Ref<Recipe[] | null>) => {
|
||||||
const localState = reactive({
|
const localState = reactive({
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { reactive, ref, useAsync } from "@nuxtjs/composition-api";
|
||||||
import { useAsyncKey } from "../use-utils";
|
import { useAsyncKey } from "../use-utils";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { VForm } from "~/types/vuetify";
|
import { VForm } from "~/types/vuetify";
|
||||||
import { RecipeTool } from "~/types/api-types/user";
|
import { RecipeTool } from "~/lib/api/types/user";
|
||||||
|
|
||||||
export const useTools = function (eager = true) {
|
export const useTools = function (eager = true) {
|
||||||
const workingToolData = reactive<RecipeTool>({
|
const workingToolData = reactive<RecipeTool>({
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ref, onMounted } from "@nuxtjs/composition-api";
|
import { ref, onMounted } from "@nuxtjs/composition-api";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export const useRecipe = function (slug: string, eager = true) {
|
export const useRecipe = function (slug: string, eager = true) {
|
||||||
const api = useUserApi();
|
const api = useUserApi();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useAsync, ref } from "@nuxtjs/composition-api";
|
import { useAsync, ref } from "@nuxtjs/composition-api";
|
||||||
import { useAsyncKey } from "../use-utils";
|
import { useAsyncKey } from "../use-utils";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export const allRecipes = ref<Recipe[]>([]);
|
export const allRecipes = ref<Recipe[]>([]);
|
||||||
export const recentRecipes = ref<Recipe[]>([]);
|
export const recentRecipes = ref<Recipe[]>([]);
|
||||||
|
@ -21,7 +21,14 @@ export const useLazyRecipes = function () {
|
||||||
tag: string | null = null,
|
tag: string | null = null,
|
||||||
tool: string | null = null
|
tool: string | null = null
|
||||||
) {
|
) {
|
||||||
const { data } = await api.recipes.getAll(page, perPage, { orderBy, orderDirection, cookbook, "categories": category, "tags": tag, "tools": tool });
|
const { data } = await api.recipes.getAll(page, perPage, {
|
||||||
|
orderBy,
|
||||||
|
orderDirection,
|
||||||
|
cookbook,
|
||||||
|
categories: category,
|
||||||
|
tags: tag,
|
||||||
|
tools: tool,
|
||||||
|
});
|
||||||
return data ? data.items : [];
|
return data ? data.items : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +61,7 @@ export const useLazyRecipes = function () {
|
||||||
appendRecipes,
|
appendRecipes,
|
||||||
assignSorted,
|
assignSorted,
|
||||||
removeRecipe,
|
removeRecipe,
|
||||||
replaceRecipes
|
replaceRecipes,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { reactive, ref, Ref } from "@nuxtjs/composition-api";
|
import { reactive, ref, Ref } from "@nuxtjs/composition-api";
|
||||||
import { useStoreActions } from "../partials/use-actions-factory";
|
import { useStoreActions } from "../partials/use-actions-factory";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { RecipeCategory } from "~/types/api-types/admin";
|
import { RecipeCategory } from "~/lib/api/types/admin";
|
||||||
|
|
||||||
const categoryStore: Ref<RecipeCategory[]> = ref([]);
|
const categoryStore: Ref<RecipeCategory[]> = ref([]);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ref, reactive, Ref } from "@nuxtjs/composition-api";
|
import { ref, reactive, Ref } from "@nuxtjs/composition-api";
|
||||||
import { useStoreActions } from "../partials/use-actions-factory";
|
import { useStoreActions } from "../partials/use-actions-factory";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { IngredientFood } from "~/types/api-types/recipe";
|
import { IngredientFood } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
let foodStore: Ref<IngredientFood[] | null> | null = null;
|
let foodStore: Ref<IngredientFood[] | null> | null = null;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { reactive, ref, Ref } from "@nuxtjs/composition-api";
|
import { reactive, ref, Ref } from "@nuxtjs/composition-api";
|
||||||
import { useStoreActions } from "../partials/use-actions-factory";
|
import { useStoreActions } from "../partials/use-actions-factory";
|
||||||
import { MultiPurposeLabelOut } from "~/types/api-types/labels";
|
import { MultiPurposeLabelOut } from "~/lib/api/types/labels";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
|
|
||||||
let labelStore: Ref<MultiPurposeLabelOut[] | null> | null = null;
|
let labelStore: Ref<MultiPurposeLabelOut[] | null> | null = null;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { reactive, ref, Ref } from "@nuxtjs/composition-api";
|
import { reactive, ref, Ref } from "@nuxtjs/composition-api";
|
||||||
import { useStoreActions } from "../partials/use-actions-factory";
|
import { useStoreActions } from "../partials/use-actions-factory";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { RecipeTag } from "~/types/api-types/admin";
|
import { RecipeTag } from "~/lib/api/types/admin";
|
||||||
|
|
||||||
const items: Ref<RecipeTag[]> = ref([]);
|
const items: Ref<RecipeTag[]> = ref([]);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { reactive, ref, Ref } from "@nuxtjs/composition-api";
|
import { reactive, ref, Ref } from "@nuxtjs/composition-api";
|
||||||
import { useStoreActions } from "../partials/use-actions-factory";
|
import { useStoreActions } from "../partials/use-actions-factory";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { RecipeTool } from "~/types/api-types/recipe";
|
import { RecipeTool } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
const toolStore: Ref<RecipeTool[]> = ref([]);
|
const toolStore: Ref<RecipeTool[]> = ref([]);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ref, reactive, Ref } from "@nuxtjs/composition-api";
|
import { ref, reactive, Ref } from "@nuxtjs/composition-api";
|
||||||
import { useStoreActions } from "../partials/use-actions-factory";
|
import { useStoreActions } from "../partials/use-actions-factory";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { IngredientUnit } from "~/types/api-types/recipe";
|
import { IngredientUnit } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
let unitStore: Ref<IngredientUnit[] | null> | null = null;
|
let unitStore: Ref<IngredientUnit[] | null> | null = null;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { useAsync, ref, reactive } from "@nuxtjs/composition-api";
|
import { useAsync, ref, reactive } from "@nuxtjs/composition-api";
|
||||||
import { toastLoading, loader } from "./use-toast";
|
import { toastLoading, loader } from "./use-toast";
|
||||||
import { AllBackups, BackupOptions } from "~/types/api-types/admin";
|
import { AllBackups, BackupOptions } from "~/lib/api/types/admin";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
|
|
||||||
interface ImportBackup {
|
interface ImportBackup {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* with the food, units, quantity, and notes.
|
* with the food, units, quantity, and notes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IngredientFood, IngredientUnit } from "~/types/api-types/recipe";
|
import { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export function getDisplayText(
|
export function getDisplayText(
|
||||||
notes = "",
|
notes = "",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useAsync, ref, Ref } from "@nuxtjs/composition-api";
|
import { useAsync, ref, Ref } from "@nuxtjs/composition-api";
|
||||||
import { useAsyncKey } from "./use-utils";
|
import { useAsyncKey } from "./use-utils";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { ReadCookBook, UpdateCookBook } from "~/types/api-types/cookbook";
|
import { ReadCookBook, UpdateCookBook } from "~/lib/api/types/cookbook";
|
||||||
|
|
||||||
let cookbookStore: Ref<ReadCookBook[] | null> | null = null;
|
let cookbookStore: Ref<ReadCookBook[] | null> | null = null;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useAsync, ref, Ref, watch } from "@nuxtjs/composition-api";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { useAsyncKey } from "./use-utils";
|
import { useAsyncKey } from "./use-utils";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { CreatePlanEntry, PlanEntryType, UpdatePlanEntry } from "~/types/api-types/meal-plan";
|
import { CreatePlanEntry, PlanEntryType, UpdatePlanEntry } from "~/lib/api/types/meal-plan";
|
||||||
|
|
||||||
export const planTypeOptions = [
|
export const planTypeOptions = [
|
||||||
{ text: "Breakfast", value: "breakfast" },
|
{ text: "Breakfast", value: "breakfast" },
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useAsync, ref } from "@nuxtjs/composition-api";
|
import { useAsync, ref } from "@nuxtjs/composition-api";
|
||||||
import { useAsyncKey } from "./use-utils";
|
import { useAsyncKey } from "./use-utils";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { ReadWebhook } from "~/types/api-types/group";
|
import { ReadWebhook } from "~/lib/api/types/group";
|
||||||
|
|
||||||
export const useGroupWebhooks = function () {
|
export const useGroupWebhooks = function () {
|
||||||
const api = useUserApi();
|
const api = useUserApi();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useAsync, ref } from "@nuxtjs/composition-api";
|
import { useAsync, ref } from "@nuxtjs/composition-api";
|
||||||
import { useAsyncKey } from "./use-utils";
|
import { useAsyncKey } from "./use-utils";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { GroupBase } from "~/types/api-types/user";
|
import { GroupBase } from "~/lib/api/types/user";
|
||||||
|
|
||||||
export const useGroupSelf = function () {
|
export const useGroupSelf = function () {
|
||||||
const api = useUserApi();
|
const api = useUserApi();
|
||||||
|
|
32
frontend/composables/use-passwords.test.ts
Normal file
32
frontend/composables/use-passwords.test.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { ref } from "@nuxtjs/composition-api";
|
||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { usePasswordStrength } from "./use-passwords";
|
||||||
|
|
||||||
|
// test("test usePasswordField", () => {
|
||||||
|
// const { inputType, togglePasswordShow, passwordIcon } = usePasswordField();
|
||||||
|
// expect(inputType.value).toBe("password");
|
||||||
|
// expect(passwordIcon.value).toBe("mdi-eye");
|
||||||
|
// togglePasswordShow();
|
||||||
|
// expect(inputType.value).toBe("text");
|
||||||
|
// expect(passwordIcon.value).toBe("mdi-eye-off");
|
||||||
|
// });
|
||||||
|
|
||||||
|
describe("test usePasswordStrength", () => {
|
||||||
|
test("weak password", () => {
|
||||||
|
const password = ref("123456");
|
||||||
|
const { score, strength, color } = usePasswordStrength(password);
|
||||||
|
expect(score.value).toBeGreaterThan(0);
|
||||||
|
expect(score.value).toBeLessThan(40);
|
||||||
|
expect(strength.value).toBe("Weak");
|
||||||
|
expect(color.value).toBe("error");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("very strong password", () => {
|
||||||
|
const password = ref("My~Secret~Not~So~Secret?123");
|
||||||
|
const { score, strength, color } = usePasswordStrength(password);
|
||||||
|
expect(score.value).toBeGreaterThan(90);
|
||||||
|
expect(score.value).toBe(100);
|
||||||
|
expect(strength.value).toBe("Very Strong");
|
||||||
|
expect(color.value).toBe("success");
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,4 +1,5 @@
|
||||||
import { computed, Ref, ref, useContext } from "@nuxtjs/composition-api";
|
import { computed, Ref, ref, useContext } from "@nuxtjs/composition-api";
|
||||||
|
import { scorePassword } from "~/lib/validators";
|
||||||
|
|
||||||
export function usePasswordField() {
|
export function usePasswordField() {
|
||||||
const show = ref(false);
|
const show = ref(false);
|
||||||
|
@ -21,46 +22,6 @@ export function usePasswordField() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function scorePassword(pass: string): number {
|
|
||||||
let score = 0;
|
|
||||||
if (!pass) return score;
|
|
||||||
|
|
||||||
const flaggedWords = ["password", "mealie", "admin", "qwerty", "login"];
|
|
||||||
|
|
||||||
if (pass.length < 6) return score;
|
|
||||||
|
|
||||||
// Check for flagged words
|
|
||||||
for (const word of flaggedWords) {
|
|
||||||
if (pass.toLowerCase().includes(word)) {
|
|
||||||
score -= 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// award every unique letter until 5 repetitions
|
|
||||||
const letters: { [key: string]: number } = {};
|
|
||||||
|
|
||||||
for (let i = 0; i < pass.length; i++) {
|
|
||||||
letters[pass[i]] = (letters[pass[i]] || 0) + 1;
|
|
||||||
score += 5.0 / letters[pass[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
// bonus points for mixing it up
|
|
||||||
const variations: { [key: string]: boolean } = {
|
|
||||||
digits: /\d/.test(pass),
|
|
||||||
lower: /[a-z]/.test(pass),
|
|
||||||
upper: /[A-Z]/.test(pass),
|
|
||||||
nonWords: /\W/.test(pass),
|
|
||||||
};
|
|
||||||
|
|
||||||
let variationCount = 0;
|
|
||||||
for (const check in variations) {
|
|
||||||
variationCount += variations[check] === true ? 1 : 0;
|
|
||||||
}
|
|
||||||
score += (variationCount - 1) * 10;
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const usePasswordStrength = (password: Ref<string>) => {
|
export const usePasswordStrength = (password: Ref<string>) => {
|
||||||
const score = computed(() => {
|
const score = computed(() => {
|
||||||
return scorePassword(password.value);
|
return scorePassword(password.value);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { useAsync, ref } from "@nuxtjs/composition-api";
|
import { useAsync, ref } from "@nuxtjs/composition-api";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { UserIn, UserOut } from "~/types/api-types/user";
|
import { UserIn, UserOut } from "~/lib/api/types/user";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: Potentially combine useAllUsers and useUser by delaying the get all users functionality
|
TODO: Potentially combine useAllUsers and useUser by delaying the get all users functionality
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
import { ref, Ref } from "@nuxtjs/composition-api";
|
import { ref, Ref } from "@nuxtjs/composition-api";
|
||||||
import { RequestResponse } from "~/types/api";
|
import { RequestResponse } from "~/lib/api/types/non-generated";
|
||||||
import { ValidationResponse } from "~/types/api-types/response";
|
import { ValidationResponse } from "~/lib/api/types/response";
|
||||||
|
import { required, email, whitespace, url, minLength, maxLength } from "~/lib/validators";
|
||||||
const EMAIL_REGEX =
|
|
||||||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@(([[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
||||||
|
|
||||||
const URL_REGEX = /[-a-zA-Z0-9@:%._+~#=]{1,256}.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
|
|
||||||
|
|
||||||
export const validators = {
|
export const validators = {
|
||||||
required: (v: string) => !!v || "This Field is Required",
|
required,
|
||||||
email: (v: string) => !v || EMAIL_REGEX.test(v) || "Email Must Be Valid",
|
email,
|
||||||
whitespace: (v: string) => !v || v.split(" ").length <= 1 || "No Whitespace Allowed",
|
whitespace,
|
||||||
url: (v: string) => !v || URL_REGEX.test(v) || "Must Be A Valid URL",
|
url,
|
||||||
minLength: (min: number) => (v: string) => !v || v.length >= min || `Must Be At Least ${min} Characters`,
|
minLength,
|
||||||
maxLength: (max: number) => (v: string) => !v || v.length <= max || `Must Be At Most ${max} Characters`,
|
maxLength,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { BaseAPI } from "../_base";
|
import { BaseAPI } from "../base/base-clients";
|
||||||
import { AdminAboutInfo, DockerVolumeText, CheckAppConfig } from "~/types/api-types/admin";
|
import { AdminAboutInfo, DockerVolumeText, CheckAppConfig } from "~/lib/api/types/admin";
|
||||||
|
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { BaseAPI } from "../_base";
|
import { BaseAPI } from "../base/base-clients";
|
||||||
import { MealieAnalytics } from "~/types/api-types/analytics";
|
import { MealieAnalytics } from "~/lib/api/types/analytics";
|
||||||
|
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { BaseAPI } from "../_base";
|
import { BaseAPI } from "../base/base-clients";
|
||||||
import { AllBackups } from "~/types/api-types/admin";
|
import { AllBackups } from "~/lib/api/types/admin";
|
||||||
import { ErrorResponse, FileTokenResponse, SuccessResponse } from "~/types/api-types/response";
|
import { ErrorResponse, FileTokenResponse, SuccessResponse } from "~/lib/api/types/response";
|
||||||
|
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { BaseCRUDAPI } from "../_base";
|
import { BaseCRUDAPI } from "../base/base-clients";
|
||||||
import { GroupBase, GroupInDB } from "~/types/api-types/user";
|
import { GroupBase, GroupInDB } from "~/lib/api/types/user";
|
||||||
import { GroupAdminUpdate } from "~/types/api-types/group";
|
import { GroupAdminUpdate } from "~/lib/api/types/group";
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
|
@ -1,6 +1,6 @@
|
||||||
import { BaseAPI } from "../_base";
|
import { BaseAPI } from "../base/base-clients";
|
||||||
import { SuccessResponse } from "~/types/api-types/response";
|
import { SuccessResponse } from "~/lib/api/types/response";
|
||||||
import { MaintenanceLogs, MaintenanceStorageDetails, MaintenanceSummary } from "~/types/api-types/admin";
|
import { MaintenanceLogs, MaintenanceStorageDetails, MaintenanceSummary } from "~/lib/api/types/admin";
|
||||||
|
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { BaseAPI } from "../_base";
|
import { BaseAPI } from "../base/base-clients";
|
||||||
import { ServerTask } from "~/types/api-types/server";
|
import { ServerTask } from "~/lib/api/types/server";
|
||||||
|
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { BaseCRUDAPI } from "../_base";
|
import { BaseCRUDAPI } from "../base/base-clients";
|
||||||
import { UnlockResults, UserIn, UserOut } from "~/types/api-types/user";
|
import { UnlockResults, UserIn, UserOut } from "~/lib/api/types/user";
|
||||||
|
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { ApiRequestInstance, PaginationData } from "~/types/api";
|
import { ApiRequestInstance, PaginationData } from "~/lib/api/types/non-generated";
|
||||||
|
|
||||||
export interface CrudAPIInterface {
|
export interface CrudAPIInterface {
|
||||||
requests: ApiRequestInstance;
|
requests: ApiRequestInstance;
|
||||||
|
@ -18,7 +18,10 @@ export abstract class BaseAPI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class BaseCRUDAPI<CreateType, ReadType, UpdateType = CreateType> extends BaseAPI implements CrudAPIInterface {
|
export abstract class BaseCRUDAPI<CreateType, ReadType, UpdateType = CreateType>
|
||||||
|
extends BaseAPI
|
||||||
|
implements CrudAPIInterface
|
||||||
|
{
|
||||||
abstract baseRoute: string;
|
abstract baseRoute: string;
|
||||||
abstract itemRoute(itemId: string | number): string;
|
abstract itemRoute(itemId: string | number): string;
|
||||||
|
|
1
frontend/lib/api/base/index.ts
Normal file
1
frontend/lib/api/base/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { route } from "./route";
|
38
frontend/lib/api/base/route.ts
Normal file
38
frontend/lib/api/base/route.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
const parts = {
|
||||||
|
host: "http://localhost.com",
|
||||||
|
prefix: "/api",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function overrideParts(host: string, prefix: string) {
|
||||||
|
parts.host = host;
|
||||||
|
parts.prefix = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type QueryValue = string | string[] | number | number[] | boolean | null | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* route is a the main URL builder for the API. It will use a predefined host and prefix (global)
|
||||||
|
* in the urls.ts file and then append the passed in path parameter uring the `URL` class from the
|
||||||
|
* browser. It will also append any query parameters passed in as the second parameter.
|
||||||
|
*
|
||||||
|
* The default host `http://localhost.com` is removed from the path if it is present. This allows us
|
||||||
|
* to bootstrap the API with different hosts as needed (like for testing) but still allows us to use
|
||||||
|
* relative URLs in production because the API and client bundle are served from the same server/host.
|
||||||
|
*/
|
||||||
|
export function route(rest: string, params: Record<string, QueryValue> | null = null): string {
|
||||||
|
const url = new URL(parts.prefix + rest, parts.host);
|
||||||
|
|
||||||
|
if (params) {
|
||||||
|
for (const [key, value] of Object.entries(params)) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
for (const item of value) {
|
||||||
|
url.searchParams.append(key, String(item));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
url.searchParams.append(key, String(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.toString().replace("http://localhost.com", "");
|
||||||
|
}
|
24
frontend/lib/api/base/routes.test.ts
Normal file
24
frontend/lib/api/base/routes.test.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { route } from ".";
|
||||||
|
|
||||||
|
describe("UrlBuilder", () => {
|
||||||
|
it("basic query parameter", () => {
|
||||||
|
const result = route("/test", { a: "b" });
|
||||||
|
expect(result).toBe("/api/test?a=b");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("multiple query parameters", () => {
|
||||||
|
const result = route("/test", { a: "b", c: "d" });
|
||||||
|
expect(result).toBe("/api/test?a=b&c=d");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("no query parameters", () => {
|
||||||
|
const result = route("/test");
|
||||||
|
expect(result).toBe("/api/test");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("list-like query parameters", () => {
|
||||||
|
const result = route("/test", { a: ["b", "c"] });
|
||||||
|
expect(result).toBe("/api/test?a=b&a=c");
|
||||||
|
});
|
||||||
|
});
|
|
@ -5,7 +5,7 @@ import { AdminGroupsApi } from "./admin/admin-groups";
|
||||||
import { AdminBackupsApi } from "./admin/admin-backups";
|
import { AdminBackupsApi } from "./admin/admin-backups";
|
||||||
import { AdminMaintenanceApi } from "./admin/admin-maintenance";
|
import { AdminMaintenanceApi } from "./admin/admin-maintenance";
|
||||||
import { AdminAnalyticsApi } from "./admin/admin-analytics";
|
import { AdminAnalyticsApi } from "./admin/admin-analytics";
|
||||||
import { ApiRequestInstance } from "~/types/api";
|
import { ApiRequestInstance } from "~/lib/api/types/non-generated";
|
||||||
|
|
||||||
export class AdminAPI {
|
export class AdminAPI {
|
||||||
public about: AdminAboutAPI;
|
public about: AdminAboutAPI;
|
|
@ -1,7 +1,7 @@
|
||||||
import { ValidatorsApi } from "./public/validators";
|
import { ValidatorsApi } from "./public/validators";
|
||||||
import { ExploreApi } from "./public/explore";
|
import { ExploreApi } from "./public/explore";
|
||||||
import { SharedApi } from "./public/shared";
|
import { SharedApi } from "./public/shared";
|
||||||
import { ApiRequestInstance } from "~/types/api";
|
import { ApiRequestInstance } from "~/lib/api/types/non-generated";
|
||||||
|
|
||||||
export class PublicApi {
|
export class PublicApi {
|
||||||
public validators: ValidatorsApi;
|
public validators: ValidatorsApi;
|
|
@ -1,33 +1,32 @@
|
||||||
import { RecipeAPI } from "./class-interfaces/recipes";
|
import { RecipeAPI } from "./user/recipes";
|
||||||
import { UserApi } from "./class-interfaces/users";
|
import { UserApi } from "./user/users";
|
||||||
import { GroupAPI } from "./class-interfaces/groups";
|
import { GroupAPI } from "./user/groups";
|
||||||
import { BackupAPI } from "./class-interfaces/backups";
|
import { BackupAPI } from "./user/backups";
|
||||||
import { UploadFile } from "./class-interfaces/upload";
|
import { UploadFile } from "./user/upload";
|
||||||
import { CategoriesAPI } from "./class-interfaces/organizer-categories";
|
import { CategoriesAPI } from "./user/organizer-categories";
|
||||||
import { TagsAPI } from "./class-interfaces/organizer-tags";
|
import { TagsAPI } from "./user/organizer-tags";
|
||||||
import { UtilsAPI } from "./class-interfaces/utils";
|
import { UtilsAPI } from "./user/utils";
|
||||||
import { FoodAPI } from "./class-interfaces/recipe-foods";
|
import { FoodAPI } from "./user/recipe-foods";
|
||||||
import { UnitAPI } from "./class-interfaces/recipe-units";
|
import { UnitAPI } from "./user/recipe-units";
|
||||||
import { CookbookAPI } from "./class-interfaces/group-cookbooks";
|
import { CookbookAPI } from "./user/group-cookbooks";
|
||||||
import { WebhooksAPI } from "./class-interfaces/group-webhooks";
|
import { WebhooksAPI } from "./user/group-webhooks";
|
||||||
import { RegisterAPI } from "./class-interfaces/user-registration";
|
import { RegisterAPI } from "./user/user-registration";
|
||||||
import { MealPlanAPI } from "./class-interfaces/group-mealplan";
|
import { MealPlanAPI } from "./user/group-mealplan";
|
||||||
import { EmailAPI } from "./class-interfaces/email";
|
import { EmailAPI } from "./user/email";
|
||||||
import { BulkActionsAPI } from "./class-interfaces/recipe-bulk-actions";
|
import { BulkActionsAPI } from "./user/recipe-bulk-actions";
|
||||||
import { GroupServerTaskAPI } from "./class-interfaces/group-tasks";
|
import { GroupServerTaskAPI } from "./user/group-tasks";
|
||||||
import { AdminAPI } from "./admin-api";
|
import { ToolsApi } from "./user/organizer-tools";
|
||||||
import { ToolsApi } from "./class-interfaces/organizer-tools";
|
import { GroupMigrationApi } from "./user/group-migrations";
|
||||||
import { GroupMigrationApi } from "./class-interfaces/group-migrations";
|
import { GroupReportsApi } from "./user/group-reports";
|
||||||
import { GroupReportsApi } from "./class-interfaces/group-reports";
|
import { ShoppingApi } from "./user/group-shopping-lists";
|
||||||
import { ShoppingApi } from "./class-interfaces/group-shopping-lists";
|
import { MultiPurposeLabelsApi } from "./user/group-multiple-purpose-labels";
|
||||||
import { MultiPurposeLabelsApi } from "./class-interfaces/group-multiple-purpose-labels";
|
import { GroupEventNotifierApi } from "./user/group-event-notifier";
|
||||||
import { GroupEventNotifierApi } from "./class-interfaces/group-event-notifier";
|
import { MealPlanRulesApi } from "./user/group-mealplan-rules";
|
||||||
import { MealPlanRulesApi } from "./class-interfaces/group-mealplan-rules";
|
import { GroupDataSeederApi } from "./user/group-seeder";
|
||||||
import { GroupDataSeederApi } from "./class-interfaces/group-seeder";
|
import { OcrAPI } from "./user/ocr";
|
||||||
import {OcrAPI} from "./class-interfaces/ocr";
|
import { ApiRequestInstance } from "~/lib/api/types/non-generated";
|
||||||
import { ApiRequestInstance } from "~/types/api";
|
|
||||||
|
|
||||||
class Api {
|
export class UserApiClient {
|
||||||
public recipes: RecipeAPI;
|
public recipes: RecipeAPI;
|
||||||
public users: UserApi;
|
public users: UserApi;
|
||||||
public groups: GroupAPI;
|
public groups: GroupAPI;
|
||||||
|
@ -98,5 +97,3 @@ class Api {
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Api, AdminAPI };
|
|
3
frontend/lib/api/index.ts
Normal file
3
frontend/lib/api/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export { AdminAPI } from "./client-admin";
|
||||||
|
export { PublicApi } from "./client-public";
|
||||||
|
export { UserApiClient as UserApi } from "./client-user";
|
|
@ -1,5 +1,5 @@
|
||||||
import { BaseAPI } from "../_base";
|
import { BaseAPI } from "../base/base-clients";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { BaseAPI } from "../_base";
|
import { BaseAPI } from "../base/base-clients";
|
||||||
import { Recipe } from "~/types/api-types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { BaseAPI } from "../_base";
|
import { BaseAPI } from "../base/base-clients";
|
||||||
import { ValidationResponse } from "~/types/api-types/response";
|
import { ValidationResponse } from "~/lib/api/types/response";
|
||||||
|
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue