playing with plugins

This commit is contained in:
vabene1111
2025-07-18 15:49:46 +02:00
parent 034d59373f
commit 2179d7d1f7
34 changed files with 1852 additions and 186 deletions

View File

@@ -38,7 +38,7 @@
</v-list-item>
<v-divider></v-divider>
<component :is="item.component" :="item" v-for="item in useNavigation().getUserNavigation()"></component>
<component :is="item.component" :="item" :key="item.title" v-for="item in useNavigation().getUserNavigation()"></component>
</v-list>
</v-menu>
</v-avatar>
@@ -79,7 +79,7 @@
<v-list-item-subtitle>{{ useUserPreferenceStore().activeSpace.name }}</v-list-item-subtitle>
</v-list-item>
<v-divider></v-divider>
<component :is="item.component" :="item" v-for="item in useNavigation().NAVIGATION_DRAWER"></component>
<component :is="item.component" :="item" :key="item.title" v-for="item in useNavigation().getNavigationDrawer()"></component>
<navigation-drawer-context-menu></navigation-drawer-context-menu>
</v-list>
@@ -113,7 +113,7 @@
<v-icon icon="fa-fw fas fa-bars"></v-icon>
<v-bottom-sheet activator="parent" close-on-content-click>
<v-list nav>
<component :is="item.component" :="item" v-for="item in useNavigation().BOTTOM_NAVIGATION"></component>
<component :is="item.component" :="item" :key="item.title" v-for="item in useNavigation().getBottomNavigation()"></component>
</v-list>
</v-bottom-sheet>
</v-btn>

View File

@@ -12,7 +12,7 @@ import { createRulesPlugin } from 'vuetify/labs/rules'
import {setupI18n} from "@/i18n";
import MealPlanPage from "@/pages/MealPlanPage.vue";
import {TandoorPlugin} from "@/types/Plugins.ts";
import {TANDOOR_PLUGINS, TandoorPlugin} from "@/types/Plugins.ts";
let routes = [
{path: '/', component: () => import("@/pages/StartPage.vue"), name: 'StartPage'},
@@ -56,11 +56,9 @@ let routes = [
{path: '/space-setup', component: () => import("@/pages/SpaceSetupPage.vue"), name: 'SpaceSetupPage'},
]
const pluginModules = import.meta.glob('@/plugins/*/plugin.ts', { eager: true })
const tandoorPlugins = [] as TandoorPlugin[]
Object.values(pluginModules).forEach(module => {
tandoorPlugins.push(module.plugin)
routes = routes.concat(module.plugin.routes)
// load plugin routes into routing table
TANDOOR_PLUGINS.forEach(plugin => {
routes = routes.concat(plugin.routes)
})
const router = createRouter({

View File

@@ -0,0 +1,39 @@
<template>
<v-select :label="$t('BaseUnit')" :hint="$t('BaseUnitHelp')" :items="BASE_UNITS" v-model="model"></v-select>
</template>
<script setup lang="ts">
import {useI18n} from "vue-i18n";
const {t} = useI18n()
const model = defineModel()
const BASE_UNITS = [
{value: "g", title: t("g")},
{value: "kg", title: t("kg")},
{value: "ounce", title: t("ounce")},
{value: "pound", title: t("pound")},
{value: "ml", title: t("ml")},
{value: "l", title: t("l")},
{value: "fluid_ounce", title: t("fluid_ounce")},
{value: "us_cup", title: t("us_cup")},
{value: "pint", title: t("pint")},
{value: "quart", title: t("quart")},
{value: "gallon", title: t("gallon")},
{value: "tbsp", title: t("tbsp")},
{value: "tsp", title: t("tsp")},
{value: "imperial_fluid_ounce", title: t("imperial_fluid_ounce")},
{value: "imperial_pint", title: t("imperial_pint")},
{value: "imperial_quart", title: t("imperial_quart")},
{value: "imperial_gallon", title: t("imperial_gallon")},
{value: "imperial_tbsp", title: t("imperial_tbsp")},
{value: "imperial_tsp", title: t("imperial_tsp")},
]
</script>
<style scoped>
</style>

View File

@@ -39,6 +39,11 @@
<model-select :label="$t('Unit')" v-model="editingObj.convertedUnit" model="Unit"></model-select>
</v-col>
</v-row>
<v-row>
<v-col>
<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-col>
</v-row>
</v-form>
</v-card-text>
</model-editor-base>

View File

@@ -14,7 +14,7 @@
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
<v-text-field :label="$t('Plural')" v-model="editingObj.pluralName"></v-text-field>
<v-textarea :label="$t('Description')" v-model="editingObj.description"></v-textarea>
<v-select :label="$t('BaseUnit')" :hint="$t('BaseUnitHelp')" :items="BASE_UNITS" v-model="editingObj.baseUnit"></v-select>
<base-unit-select v-model="editingObj.baseUnit"></base-unit-select>
<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>
@@ -29,6 +29,7 @@ import {Unit} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
import {useI18n} from "vue-i18n";
import BaseUnitSelect from "@/components/inputs/BaseUnitSelect.vue";
const {t} = useI18n()
@@ -51,27 +52,7 @@ watch([() => props.item, () => props.itemId], () => {
// object specific data (for selects/display)
const BASE_UNITS = [
{value: "g", title: t("g")},
{value: "kg", title: t("kg")},
{value: "ounce", title: t("ounce")},
{value: "pound", title: t("pound")},
{value: "ml", title: t("ml")},
{value: "l", title: t("l")},
{value: "fluid_ounce", title: t("fluid_ounce")},
{value: "us_cup", title: t("us_cup")},
{value: "pint", title: t("pint")},
{value: "quart", title: t("quart")},
{value: "gallon", title: t("gallon")},
{value: "tbsp", title: t("tbsp")},
{value: "tsp", title: t("tsp")},
{value: "imperial_fluid_ounce", title: t("imperial_fluid_ounce")},
{value: "imperial_pint", title: t("imperial_pint")},
{value: "imperial_quart", title: t("imperial_quart")},
{value: "imperial_gallon", title: t("imperial_gallon")},
{value: "imperial_tbsp", title: t("imperial_tbsp")},
{value: "imperial_tsp", title: t("imperial_tsp")},
]
onMounted(() => {
initializeEditor()

View File

@@ -2,6 +2,8 @@ import {useI18n} from "vue-i18n";
import {VDivider, VListItem} from "vuetify/components";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
import {useDjangoUrls} from "@/composables/useDjangoUrls.ts";
import {TANDOOR_PLUGINS} from "@/types/Plugins.ts";
import {plugin} from "@/plugins/open_data_plugin/plugin.ts";
/**
* manages configuration and loading of navigation entries for tandoor main app and plugins
@@ -9,27 +11,46 @@ import {useDjangoUrls} from "@/composables/useDjangoUrls.ts";
export function useNavigation() {
const {t} = useI18n()
let NAVIGATION_DRAWER = [
{component: VListItem, prependIcon: '$recipes', title: 'Home', to: {name: 'StartPage', params: {}}},
{component: VListItem, prependIcon: '$search', title: t('Search'), to: {name: 'SearchPage', params: {}}},
{component: VListItem, prependIcon: '$mealplan', title: t('Meal_Plan'), to: {name: 'MealPlanPage', params: {}}},
{component: VListItem, prependIcon: '$shopping', title: t('Shopping_list'), to: {name: 'ShoppingListPage', params: {}}},
{component: VListItem, prependIcon: 'fas fa-globe', title: t('Import'), to: {name: 'RecipeImportPage', params: {}}},
{component: VListItem, prependIcon: '$books', title: t('Books'), to: {name: 'BooksPage', params: {}}},
{component: VListItem, prependIcon: 'fa-solid fa-folder-tree', title: t('DatabasePage'), to: {name: 'SearchPage', params: {}}},
]
function getNavigationDrawer() {
let navigation = [
{component: VListItem, prependIcon: '$recipes', title: 'Home', to: {name: 'StartPage', params: {}}},
{component: VListItem, prependIcon: '$search', title: t('Search'), to: {name: 'SearchPage', params: {}}},
{component: VListItem, prependIcon: '$mealplan', title: t('Meal_Plan'), to: {name: 'MealPlanPage', params: {}}},
{component: VListItem, prependIcon: '$shopping', title: t('Shopping_list'), to: {name: 'ShoppingListPage', params: {}}},
{component: VListItem, prependIcon: 'fas fa-globe', title: t('Import'), to: {name: 'RecipeImportPage', params: {}}},
{component: VListItem, prependIcon: '$books', title: t('Books'), to: {name: 'BooksPage', params: {}}},
{component: VListItem, prependIcon: 'fa-solid fa-folder-tree', title: t('Database'), to: {name: 'DatabasePage', params: {}}},
]
let BOTTOM_NAVIGATION = [
{component: VListItem, prependIcon: 'fa-solid fa-sliders', title: t('Settings'), to: {name: 'SettingsPage', params: {}}},
{component: VListItem, prependIcon: 'fas fa-globe', title: t('Import'), to: {name: 'RecipeImportPage', params: {}}},
{component: VListItem, prependIcon: 'fa-solid fa-folder-tree', title: t('Database'), to: {name: 'DatabasePage', params: {}}},
{component: VListItem, prependIcon: '$books', title: t('Books'), to: {name: 'BooksPage', params: {}}},
]
TANDOOR_PLUGINS.forEach(plugin => {
plugin.navigationDrawer.forEach(navEntry => {
let navEntryCopy = Object.assign({}, navEntry)
navEntryCopy.title = t(navEntryCopy.title)
navigation.push(navEntryCopy)
})
})
let USER_NAVIGATION = [
{component: VListItem, prependIcon: 'fa-solid fa-sliders', title: t('Settings'), to: {name: 'SettingsPage', params: {}}},
{component: VListItem, prependIcon: 'fa-solid fa-question', title: t('Settings'), to: {name: 'HelpPage', params: {}}},
]
return navigation
}
function getBottomNavigation() {
let navigation = [
{component: VListItem, prependIcon: 'fa-solid fa-sliders', title: t('Settings'), to: {name: 'SettingsPage', params: {}}},
{component: VListItem, prependIcon: 'fas fa-globe', title: t('Import'), to: {name: 'RecipeImportPage', params: {}}},
{component: VListItem, prependIcon: 'fa-solid fa-folder-tree', title: t('Database'), to: {name: 'DatabasePage', params: {}}},
{component: VListItem, prependIcon: '$books', title: t('Books'), to: {name: 'BooksPage', params: {}}},
]
TANDOOR_PLUGINS.forEach(plugin => {
plugin.bottomNavigation.forEach(navEntry => {
let navEntryCopy = Object.assign({}, navEntry)
navEntryCopy.title = t(navEntryCopy.title)
navigation.push(navEntryCopy)
})
})
return navigation
}
function getUserNavigation() {
let navigation = []
@@ -56,26 +77,18 @@ export function useNavigation() {
navigation.push({component: VDivider})
}
TANDOOR_PLUGINS.forEach(plugin => {
plugin.userNavigation.forEach(navEntry => {
let navEntryCopy = Object.assign({}, navEntry)
navEntryCopy.title = t(navEntryCopy.title)
navigation.push(navEntryCopy)
})
})
navigation.push({component: VListItem, prependIcon: 'fa-solid fa-arrow-right-from-bracket', title: t('Logout'), href: useDjangoUrls().getDjangoUrl('accounts/logout')})
return navigation
}
return {NAVIGATION_DRAWER, BOTTOM_NAVIGATION, USER_NAVIGATION, getUserNavigation}
}
//
// <v-list-item :href="getDjangoUrl('admin')" target="_blank" v-if="useUserPreferenceStore().userSettings.user.isSuperuser">
// <template #prepend>
// <v-icon icon="fa-solid fa-shield"></v-icon>
// </template>
// {{ $t('Admin') }}
// </v-list-item>
// <v-list-item :href="getDjangoUrl('accounts/logout')" link>
// <template #prepend>
// <v-icon icon="fa-solid fa-arrow-right-from-bracket"></v-icon>
// </template>
// {{ $t('Logout') }}
// </v-list-item>
return {getNavigationDrawer, getBottomNavigation, getUserNavigation}
}

View File

@@ -7,6 +7,7 @@ import type {
import {createI18n} from "vue-i18n";
import en from "../../vue3/src/locales/en.json";
import {TANDOOR_PLUGINS} from "@/types/Plugins.ts";
/**
* lazy loading of translation, resources:
@@ -31,10 +32,17 @@ export function setupI18n() {
locale: 'en',
fallbackLocale: 'en',
messages: {
en
en,
},
}) as I18n
// async load plugin default locales
TANDOOR_PLUGINS.forEach(plugin => {
plugin.defaultLocale.then(pluginMessages => {
i18n.global.mergeLocaleMessage('en', pluginMessages)
})
})
// async load user locale into existing i18n instance
loadLocaleMessages(i18n, locale).then()
@@ -48,9 +56,10 @@ export function setupI18n() {
*/
export async function loadLocaleMessages(i18n: I18n, locale: Locale) {
// load locale messages
const messages = await import(`./locales/${locale}.json`).then(
(r: any) => r.default || r
)
let messages = en
if (locale != 'en') {
messages = await import(`./locales/${locale}.json`).then((r: any) => r.default || r)
}
// remove empty strings
Object.entries(messages).forEach(([key, value]) => {
@@ -62,6 +71,25 @@ export async function loadLocaleMessages(i18n: I18n, locale: Locale) {
// set messages for locale
i18n.global.setLocaleMessage(locale, messages)
// async load and merge messages from plugins
TANDOOR_PLUGINS.forEach(plugin => {
let pluginLocales = getSupportedLocales(plugin.localeFiles)
if (pluginLocales.includes(locale)) {
import(`@/plugins/${plugin.basePath}/locales/${locale}.json`).then((r: any) => {
let pluginMessages = r.default || r
// remove empty strings
Object.entries(pluginMessages).forEach(([key, value]) => {
if (value === '') {
delete pluginMessages[key]
}
})
i18n.global.mergeLocaleMessage(locale, pluginMessages)
})
}
})
// switch to given locale
setLocale(i18n, locale)
}
@@ -69,10 +97,11 @@ export async function loadLocaleMessages(i18n: I18n, locale: Locale) {
/**
* loop trough translation files to determine for which locales a translation is available
* @return string[] of supported locales
* @param localeFiles module import of locale files to loop trough
*/
function getSupportedLocales() {
function getSupportedLocales(localeFiles = import.meta.glob('@/locales/*.json')) {
let supportedLocales: string[] = []
let localeFiles = import.meta.glob('@/locales/*.json');
for (const path in localeFiles) {
supportedLocales.push(path.split('/').slice(-1)[0].split('.')[0]);
}

View File

@@ -71,6 +71,7 @@
"Combine_All_Steps": "Combine all steps into a single field.",
"Coming_Soon": "Coming-Soon",
"Comments_setting": "Show Comments",
"Comment": "Comment",
"Completed": "Completed",
"Confirm": "Confirm",
"ConnectorConfig": "Connectors",
@@ -459,6 +460,7 @@
"Size": "Size",
"Social_Authentication": "Social Authentication",
"Sort_by_new": "Sort by new",
"Source": "Source",
"SourceImportHelp": "Import JSON in schema.org/recipe format or html pages with json+ld recipe or microdata.",
"SourceImportSubtitle": "Import JSON or HTML manually.",
"SpaceLimitExceeded": "Your space has surpassed one of its limits, some functions might be restricted.",

View File

@@ -64,6 +64,13 @@ models/PaginatedInviteLinkList.ts
models/PaginatedKeywordList.ts
models/PaginatedMealPlanList.ts
models/PaginatedMealTypeList.ts
models/PaginatedOpenDataCategoryList.ts
models/PaginatedOpenDataConversionList.ts
models/PaginatedOpenDataFoodList.ts
models/PaginatedOpenDataPropertyList.ts
models/PaginatedOpenDataStoreList.ts
models/PaginatedOpenDataUnitList.ts
models/PaginatedOpenDataVersionList.ts
models/PaginatedPropertyList.ts
models/PaginatedPropertyTypeList.ts
models/PaginatedRecipeBookEntryList.ts

View File

@@ -60,6 +60,13 @@ import type {
PaginatedKeywordList,
PaginatedMealPlanList,
PaginatedMealTypeList,
PaginatedOpenDataCategoryList,
PaginatedOpenDataConversionList,
PaginatedOpenDataFoodList,
PaginatedOpenDataPropertyList,
PaginatedOpenDataStoreList,
PaginatedOpenDataUnitList,
PaginatedOpenDataVersionList,
PaginatedPropertyList,
PaginatedPropertyTypeList,
PaginatedRecipeBookEntryList,
@@ -252,6 +259,20 @@ import {
PaginatedMealPlanListToJSON,
PaginatedMealTypeListFromJSON,
PaginatedMealTypeListToJSON,
PaginatedOpenDataCategoryListFromJSON,
PaginatedOpenDataCategoryListToJSON,
PaginatedOpenDataConversionListFromJSON,
PaginatedOpenDataConversionListToJSON,
PaginatedOpenDataFoodListFromJSON,
PaginatedOpenDataFoodListToJSON,
PaginatedOpenDataPropertyListFromJSON,
PaginatedOpenDataPropertyListToJSON,
PaginatedOpenDataStoreListFromJSON,
PaginatedOpenDataStoreListToJSON,
PaginatedOpenDataUnitListFromJSON,
PaginatedOpenDataUnitListToJSON,
PaginatedOpenDataVersionListFromJSON,
PaginatedOpenDataVersionListToJSON,
PaginatedPropertyListFromJSON,
PaginatedPropertyListToJSON,
PaginatedPropertyTypeListFromJSON,
@@ -955,6 +976,11 @@ export interface ApiOpenDataCategoryDestroyRequest {
id: number;
}
export interface ApiOpenDataCategoryListRequest {
page?: number;
pageSize?: number;
}
export interface ApiOpenDataCategoryPartialUpdateRequest {
id: number;
patchedOpenDataCategory?: Omit<PatchedOpenDataCategory, 'createdBy'>;
@@ -977,6 +1003,11 @@ export interface ApiOpenDataConversionDestroyRequest {
id: number;
}
export interface ApiOpenDataConversionListRequest {
page?: number;
pageSize?: number;
}
export interface ApiOpenDataConversionPartialUpdateRequest {
id: number;
patchedOpenDataConversion?: Omit<PatchedOpenDataConversion, 'createdBy'>;
@@ -1003,6 +1034,16 @@ export interface ApiOpenDataFoodDestroyRequest {
id: number;
}
export interface ApiOpenDataFoodFdcCreateRequest {
id: number;
openDataFood: Omit<OpenDataFood, 'createdBy'>;
}
export interface ApiOpenDataFoodListRequest {
page?: number;
pageSize?: number;
}
export interface ApiOpenDataFoodPartialUpdateRequest {
id: number;
patchedOpenDataFood?: Omit<PatchedOpenDataFood, 'createdBy'>;
@@ -1025,6 +1066,11 @@ export interface ApiOpenDataPropertyDestroyRequest {
id: number;
}
export interface ApiOpenDataPropertyListRequest {
page?: number;
pageSize?: number;
}
export interface ApiOpenDataPropertyPartialUpdateRequest {
id: number;
patchedOpenDataProperty?: Omit<PatchedOpenDataProperty, 'createdBy'>;
@@ -1047,6 +1093,11 @@ export interface ApiOpenDataStoreDestroyRequest {
id: number;
}
export interface ApiOpenDataStoreListRequest {
page?: number;
pageSize?: number;
}
export interface ApiOpenDataStorePartialUpdateRequest {
id: number;
patchedOpenDataStore?: Omit<PatchedOpenDataStore, 'createdBy'>;
@@ -1069,6 +1120,11 @@ export interface ApiOpenDataUnitDestroyRequest {
id: number;
}
export interface ApiOpenDataUnitListRequest {
page?: number;
pageSize?: number;
}
export interface ApiOpenDataUnitPartialUpdateRequest {
id: number;
patchedOpenDataUnit?: Omit<PatchedOpenDataUnit, 'createdBy'>;
@@ -1091,6 +1147,11 @@ export interface ApiOpenDataVersionDestroyRequest {
id: number;
}
export interface ApiOpenDataVersionListRequest {
page?: number;
pageSize?: number;
}
export interface ApiOpenDataVersionPartialUpdateRequest {
id: number;
patchedOpenDataVersion?: PatchedOpenDataVersion;
@@ -6324,9 +6385,17 @@ export class ApiApi extends runtime.BaseAPI {
/**
*/
async apiOpenDataCategoryListRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Array<OpenDataCategory>>> {
async apiOpenDataCategoryListRaw(requestParameters: ApiOpenDataCategoryListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedOpenDataCategoryList>> {
const queryParameters: any = {};
if (requestParameters['page'] != null) {
queryParameters['page'] = requestParameters['page'];
}
if (requestParameters['pageSize'] != null) {
queryParameters['page_size'] = requestParameters['pageSize'];
}
const headerParameters: runtime.HTTPHeaders = {};
if (this.configuration && this.configuration.apiKey) {
@@ -6340,13 +6409,13 @@ export class ApiApi extends runtime.BaseAPI {
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(OpenDataCategoryFromJSON));
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedOpenDataCategoryListFromJSON(jsonValue));
}
/**
*/
async apiOpenDataCategoryList(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Array<OpenDataCategory>> {
const response = await this.apiOpenDataCategoryListRaw(initOverrides);
async apiOpenDataCategoryList(requestParameters: ApiOpenDataCategoryListRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedOpenDataCategoryList> {
const response = await this.apiOpenDataCategoryListRaw(requestParameters, initOverrides);
return await response.value();
}
@@ -6542,9 +6611,17 @@ export class ApiApi extends runtime.BaseAPI {
/**
*/
async apiOpenDataConversionListRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Array<OpenDataConversion>>> {
async apiOpenDataConversionListRaw(requestParameters: ApiOpenDataConversionListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedOpenDataConversionList>> {
const queryParameters: any = {};
if (requestParameters['page'] != null) {
queryParameters['page'] = requestParameters['page'];
}
if (requestParameters['pageSize'] != null) {
queryParameters['page_size'] = requestParameters['pageSize'];
}
const headerParameters: runtime.HTTPHeaders = {};
if (this.configuration && this.configuration.apiKey) {
@@ -6558,13 +6635,13 @@ export class ApiApi extends runtime.BaseAPI {
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(OpenDataConversionFromJSON));
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedOpenDataConversionListFromJSON(jsonValue));
}
/**
*/
async apiOpenDataConversionList(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Array<OpenDataConversion>> {
const response = await this.apiOpenDataConversionListRaw(initOverrides);
async apiOpenDataConversionList(requestParameters: ApiOpenDataConversionListRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedOpenDataConversionList> {
const response = await this.apiOpenDataConversionListRaw(requestParameters, initOverrides);
return await response.value();
}
@@ -6793,12 +6870,67 @@ export class ApiApi extends runtime.BaseAPI {
}
/**
* updates the food with all possible data from the FDC Api if properties with a fdc_id already exist they will be overridden, if existing properties don\'t have a fdc_id they won\'t be changed
*/
async apiOpenDataFoodListRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Array<OpenDataFood>>> {
async apiOpenDataFoodFdcCreateRaw(requestParameters: ApiOpenDataFoodFdcCreateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<OpenDataFood>> {
if (requestParameters['id'] == null) {
throw new runtime.RequiredError(
'id',
'Required parameter "id" was null or undefined when calling apiOpenDataFoodFdcCreate().'
);
}
if (requestParameters['openDataFood'] == null) {
throw new runtime.RequiredError(
'openDataFood',
'Required parameter "openDataFood" was null or undefined when calling apiOpenDataFoodFdcCreate().'
);
}
const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {};
headerParameters['Content-Type'] = 'application/json';
if (this.configuration && this.configuration.apiKey) {
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
}
const response = await this.request({
path: `/api/open-data-food/{id}/fdc/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
method: 'POST',
headers: headerParameters,
query: queryParameters,
body: OpenDataFoodToJSON(requestParameters['openDataFood']),
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => OpenDataFoodFromJSON(jsonValue));
}
/**
* updates the food with all possible data from the FDC Api if properties with a fdc_id already exist they will be overridden, if existing properties don\'t have a fdc_id they won\'t be changed
*/
async apiOpenDataFoodFdcCreate(requestParameters: ApiOpenDataFoodFdcCreateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<OpenDataFood> {
const response = await this.apiOpenDataFoodFdcCreateRaw(requestParameters, initOverrides);
return await response.value();
}
/**
*/
async apiOpenDataFoodListRaw(requestParameters: ApiOpenDataFoodListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedOpenDataFoodList>> {
const queryParameters: any = {};
if (requestParameters['page'] != null) {
queryParameters['page'] = requestParameters['page'];
}
if (requestParameters['pageSize'] != null) {
queryParameters['page_size'] = requestParameters['pageSize'];
}
const headerParameters: runtime.HTTPHeaders = {};
if (this.configuration && this.configuration.apiKey) {
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
}
@@ -6810,13 +6942,13 @@ export class ApiApi extends runtime.BaseAPI {
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(OpenDataFoodFromJSON));
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedOpenDataFoodListFromJSON(jsonValue));
}
/**
*/
async apiOpenDataFoodList(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Array<OpenDataFood>> {
const response = await this.apiOpenDataFoodListRaw(initOverrides);
async apiOpenDataFoodList(requestParameters: ApiOpenDataFoodListRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedOpenDataFoodList> {
const response = await this.apiOpenDataFoodListRaw(requestParameters, initOverrides);
return await response.value();
}
@@ -7012,9 +7144,17 @@ export class ApiApi extends runtime.BaseAPI {
/**
*/
async apiOpenDataPropertyListRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Array<OpenDataProperty>>> {
async apiOpenDataPropertyListRaw(requestParameters: ApiOpenDataPropertyListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedOpenDataPropertyList>> {
const queryParameters: any = {};
if (requestParameters['page'] != null) {
queryParameters['page'] = requestParameters['page'];
}
if (requestParameters['pageSize'] != null) {
queryParameters['page_size'] = requestParameters['pageSize'];
}
const headerParameters: runtime.HTTPHeaders = {};
if (this.configuration && this.configuration.apiKey) {
@@ -7028,13 +7168,13 @@ export class ApiApi extends runtime.BaseAPI {
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(OpenDataPropertyFromJSON));
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedOpenDataPropertyListFromJSON(jsonValue));
}
/**
*/
async apiOpenDataPropertyList(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Array<OpenDataProperty>> {
const response = await this.apiOpenDataPropertyListRaw(initOverrides);
async apiOpenDataPropertyList(requestParameters: ApiOpenDataPropertyListRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedOpenDataPropertyList> {
const response = await this.apiOpenDataPropertyListRaw(requestParameters, initOverrides);
return await response.value();
}
@@ -7257,9 +7397,17 @@ export class ApiApi extends runtime.BaseAPI {
/**
*/
async apiOpenDataStoreListRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Array<OpenDataStore>>> {
async apiOpenDataStoreListRaw(requestParameters: ApiOpenDataStoreListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedOpenDataStoreList>> {
const queryParameters: any = {};
if (requestParameters['page'] != null) {
queryParameters['page'] = requestParameters['page'];
}
if (requestParameters['pageSize'] != null) {
queryParameters['page_size'] = requestParameters['pageSize'];
}
const headerParameters: runtime.HTTPHeaders = {};
if (this.configuration && this.configuration.apiKey) {
@@ -7273,13 +7421,13 @@ export class ApiApi extends runtime.BaseAPI {
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(OpenDataStoreFromJSON));
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedOpenDataStoreListFromJSON(jsonValue));
}
/**
*/
async apiOpenDataStoreList(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Array<OpenDataStore>> {
const response = await this.apiOpenDataStoreListRaw(initOverrides);
async apiOpenDataStoreList(requestParameters: ApiOpenDataStoreListRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedOpenDataStoreList> {
const response = await this.apiOpenDataStoreListRaw(requestParameters, initOverrides);
return await response.value();
}
@@ -7475,9 +7623,17 @@ export class ApiApi extends runtime.BaseAPI {
/**
*/
async apiOpenDataUnitListRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Array<OpenDataUnit>>> {
async apiOpenDataUnitListRaw(requestParameters: ApiOpenDataUnitListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedOpenDataUnitList>> {
const queryParameters: any = {};
if (requestParameters['page'] != null) {
queryParameters['page'] = requestParameters['page'];
}
if (requestParameters['pageSize'] != null) {
queryParameters['page_size'] = requestParameters['pageSize'];
}
const headerParameters: runtime.HTTPHeaders = {};
if (this.configuration && this.configuration.apiKey) {
@@ -7491,13 +7647,13 @@ export class ApiApi extends runtime.BaseAPI {
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(OpenDataUnitFromJSON));
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedOpenDataUnitListFromJSON(jsonValue));
}
/**
*/
async apiOpenDataUnitList(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Array<OpenDataUnit>> {
const response = await this.apiOpenDataUnitListRaw(initOverrides);
async apiOpenDataUnitList(requestParameters: ApiOpenDataUnitListRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedOpenDataUnitList> {
const response = await this.apiOpenDataUnitListRaw(requestParameters, initOverrides);
return await response.value();
}
@@ -7693,9 +7849,17 @@ export class ApiApi extends runtime.BaseAPI {
/**
*/
async apiOpenDataVersionListRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Array<OpenDataVersion>>> {
async apiOpenDataVersionListRaw(requestParameters: ApiOpenDataVersionListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedOpenDataVersionList>> {
const queryParameters: any = {};
if (requestParameters['page'] != null) {
queryParameters['page'] = requestParameters['page'];
}
if (requestParameters['pageSize'] != null) {
queryParameters['page_size'] = requestParameters['pageSize'];
}
const headerParameters: runtime.HTTPHeaders = {};
if (this.configuration && this.configuration.apiKey) {
@@ -7709,13 +7873,13 @@ export class ApiApi extends runtime.BaseAPI {
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(OpenDataVersionFromJSON));
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedOpenDataVersionListFromJSON(jsonValue));
}
/**
*/
async apiOpenDataVersionList(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Array<OpenDataVersion>> {
const response = await this.apiOpenDataVersionListRaw(initOverrides);
async apiOpenDataVersionList(requestParameters: ApiOpenDataVersionListRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedOpenDataVersionList> {
const response = await this.apiOpenDataVersionListRaw(requestParameters, initOverrides);
return await response.value();
}

View File

@@ -14,43 +14,47 @@
/**
* * `G` - g
* * `KG` - kg
* * `ML` - ml
* * `L` - l
* * `OUNCE` - ounce
* * `POUND` - pound
* * `FLUID_OUNCE` - fluid_ounce
* * `TSP` - tsp
* * `TBSP` - tbsp
* * `CUP` - cup
* * `PINT` - pint
* * `QUART` - quart
* * `GALLON` - gallon
* * `IMPERIAL_FLUID_OUNCE` - imperial fluid ounce
* * `IMPERIAL_PINT` - imperial pint
* * `IMPERIAL_QUART` - imperial quart
* * `IMPERIAL_GALLON` - imperial gallon
* * `g` - g
* * `kg` - kg
* * `ounce` - ounce
* * `pound` - pound
* * `ml` - ml
* * `l` - l
* * `fluid_ounce` - fluid_ounce
* * `pint` - pint
* * `quart` - quart
* * `gallon` - gallon
* * `tbsp` - tbsp
* * `tsp` - tsp
* * `us_cup` - US Cup
* * `imperial_fluid_ounce` - imperial fluid ounce
* * `imperial_pint` - imperial pint
* * `imperial_quart` - imperial quart
* * `imperial_gallon` - imperial gallon
* * `imperial_tbsp` - imperial tbsp
* * `imperial_tsp` - imperial tsp
* @export
*/
export const BaseUnitEnum = {
G: 'G',
Kg: 'KG',
Ml: 'ML',
L: 'L',
Ounce: 'OUNCE',
Pound: 'POUND',
FluidOunce: 'FLUID_OUNCE',
Tsp: 'TSP',
Tbsp: 'TBSP',
Cup: 'CUP',
Pint: 'PINT',
Quart: 'QUART',
Gallon: 'GALLON',
ImperialFluidOunce: 'IMPERIAL_FLUID_OUNCE',
ImperialPint: 'IMPERIAL_PINT',
ImperialQuart: 'IMPERIAL_QUART',
ImperialGallon: 'IMPERIAL_GALLON'
G: 'g',
Kg: 'kg',
Ounce: 'ounce',
Pound: 'pound',
Ml: 'ml',
L: 'l',
FluidOunce: 'fluid_ounce',
Pint: 'pint',
Quart: 'quart',
Gallon: 'gallon',
Tbsp: 'tbsp',
Tsp: 'tsp',
UsCup: 'us_cup',
ImperialFluidOunce: 'imperial_fluid_ounce',
ImperialPint: 'imperial_pint',
ImperialQuart: 'imperial_quart',
ImperialGallon: 'imperial_gallon',
ImperialTbsp: 'imperial_tbsp',
ImperialTsp: 'imperial_tsp'
} as const;
export type BaseUnitEnum = typeof BaseUnitEnum[keyof typeof BaseUnitEnum];

View File

@@ -164,10 +164,10 @@ export interface OpenDataFood {
propertiesSource?: string;
/**
*
* @type {string}
* @type {number}
* @memberof OpenDataFood
*/
fdcId: string;
fdcId?: number;
/**
*
* @type {string}
@@ -193,7 +193,6 @@ export function instanceOfOpenDataFood(value: object): value is OpenDataFood {
if (!('storeCategory' in value) || value['storeCategory'] === undefined) return false;
if (!('properties' in value) || value['properties'] === undefined) return false;
if (!('propertiesFoodUnit' in value) || value['propertiesFoodUnit'] === undefined) return false;
if (!('fdcId' in value) || value['fdcId'] === undefined) return false;
if (!('createdBy' in value) || value['createdBy'] === undefined) return false;
return true;
}
@@ -222,7 +221,7 @@ export function OpenDataFoodFromJSONTyped(json: any, ignoreDiscriminator: boolea
'propertiesFoodAmount': json['properties_food_amount'] == null ? undefined : json['properties_food_amount'],
'propertiesFoodUnit': OpenDataUnitFromJSON(json['properties_food_unit']),
'propertiesSource': json['properties_source'] == null ? undefined : json['properties_source'],
'fdcId': json['fdc_id'],
'fdcId': json['fdc_id'] == null ? undefined : json['fdc_id'],
'comment': json['comment'] == null ? undefined : json['comment'],
'createdBy': json['created_by'],
};

View File

@@ -0,0 +1,101 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { OpenDataCategory } from './OpenDataCategory';
import {
OpenDataCategoryFromJSON,
OpenDataCategoryFromJSONTyped,
OpenDataCategoryToJSON,
} from './OpenDataCategory';
/**
*
* @export
* @interface PaginatedOpenDataCategoryList
*/
export interface PaginatedOpenDataCategoryList {
/**
*
* @type {number}
* @memberof PaginatedOpenDataCategoryList
*/
count: number;
/**
*
* @type {string}
* @memberof PaginatedOpenDataCategoryList
*/
next?: string;
/**
*
* @type {string}
* @memberof PaginatedOpenDataCategoryList
*/
previous?: string;
/**
*
* @type {Array<OpenDataCategory>}
* @memberof PaginatedOpenDataCategoryList
*/
results: Array<OpenDataCategory>;
/**
*
* @type {Date}
* @memberof PaginatedOpenDataCategoryList
*/
timestamp?: Date;
}
/**
* Check if a given object implements the PaginatedOpenDataCategoryList interface.
*/
export function instanceOfPaginatedOpenDataCategoryList(value: object): value is PaginatedOpenDataCategoryList {
if (!('count' in value) || value['count'] === undefined) return false;
if (!('results' in value) || value['results'] === undefined) return false;
return true;
}
export function PaginatedOpenDataCategoryListFromJSON(json: any): PaginatedOpenDataCategoryList {
return PaginatedOpenDataCategoryListFromJSONTyped(json, false);
}
export function PaginatedOpenDataCategoryListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PaginatedOpenDataCategoryList {
if (json == null) {
return json;
}
return {
'count': json['count'],
'next': json['next'] == null ? undefined : json['next'],
'previous': json['previous'] == null ? undefined : json['previous'],
'results': ((json['results'] as Array<any>).map(OpenDataCategoryFromJSON)),
'timestamp': json['timestamp'] == null ? undefined : (new Date(json['timestamp'])),
};
}
export function PaginatedOpenDataCategoryListToJSON(value?: PaginatedOpenDataCategoryList | null): any {
if (value == null) {
return value;
}
return {
'count': value['count'],
'next': value['next'],
'previous': value['previous'],
'results': ((value['results'] as Array<any>).map(OpenDataCategoryToJSON)),
'timestamp': value['timestamp'] == null ? undefined : ((value['timestamp']).toISOString()),
};
}

View File

@@ -0,0 +1,101 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { OpenDataConversion } from './OpenDataConversion';
import {
OpenDataConversionFromJSON,
OpenDataConversionFromJSONTyped,
OpenDataConversionToJSON,
} from './OpenDataConversion';
/**
*
* @export
* @interface PaginatedOpenDataConversionList
*/
export interface PaginatedOpenDataConversionList {
/**
*
* @type {number}
* @memberof PaginatedOpenDataConversionList
*/
count: number;
/**
*
* @type {string}
* @memberof PaginatedOpenDataConversionList
*/
next?: string;
/**
*
* @type {string}
* @memberof PaginatedOpenDataConversionList
*/
previous?: string;
/**
*
* @type {Array<OpenDataConversion>}
* @memberof PaginatedOpenDataConversionList
*/
results: Array<OpenDataConversion>;
/**
*
* @type {Date}
* @memberof PaginatedOpenDataConversionList
*/
timestamp?: Date;
}
/**
* Check if a given object implements the PaginatedOpenDataConversionList interface.
*/
export function instanceOfPaginatedOpenDataConversionList(value: object): value is PaginatedOpenDataConversionList {
if (!('count' in value) || value['count'] === undefined) return false;
if (!('results' in value) || value['results'] === undefined) return false;
return true;
}
export function PaginatedOpenDataConversionListFromJSON(json: any): PaginatedOpenDataConversionList {
return PaginatedOpenDataConversionListFromJSONTyped(json, false);
}
export function PaginatedOpenDataConversionListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PaginatedOpenDataConversionList {
if (json == null) {
return json;
}
return {
'count': json['count'],
'next': json['next'] == null ? undefined : json['next'],
'previous': json['previous'] == null ? undefined : json['previous'],
'results': ((json['results'] as Array<any>).map(OpenDataConversionFromJSON)),
'timestamp': json['timestamp'] == null ? undefined : (new Date(json['timestamp'])),
};
}
export function PaginatedOpenDataConversionListToJSON(value?: PaginatedOpenDataConversionList | null): any {
if (value == null) {
return value;
}
return {
'count': value['count'],
'next': value['next'],
'previous': value['previous'],
'results': ((value['results'] as Array<any>).map(OpenDataConversionToJSON)),
'timestamp': value['timestamp'] == null ? undefined : ((value['timestamp']).toISOString()),
};
}

View File

@@ -0,0 +1,101 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { OpenDataFood } from './OpenDataFood';
import {
OpenDataFoodFromJSON,
OpenDataFoodFromJSONTyped,
OpenDataFoodToJSON,
} from './OpenDataFood';
/**
*
* @export
* @interface PaginatedOpenDataFoodList
*/
export interface PaginatedOpenDataFoodList {
/**
*
* @type {number}
* @memberof PaginatedOpenDataFoodList
*/
count: number;
/**
*
* @type {string}
* @memberof PaginatedOpenDataFoodList
*/
next?: string;
/**
*
* @type {string}
* @memberof PaginatedOpenDataFoodList
*/
previous?: string;
/**
*
* @type {Array<OpenDataFood>}
* @memberof PaginatedOpenDataFoodList
*/
results: Array<OpenDataFood>;
/**
*
* @type {Date}
* @memberof PaginatedOpenDataFoodList
*/
timestamp?: Date;
}
/**
* Check if a given object implements the PaginatedOpenDataFoodList interface.
*/
export function instanceOfPaginatedOpenDataFoodList(value: object): value is PaginatedOpenDataFoodList {
if (!('count' in value) || value['count'] === undefined) return false;
if (!('results' in value) || value['results'] === undefined) return false;
return true;
}
export function PaginatedOpenDataFoodListFromJSON(json: any): PaginatedOpenDataFoodList {
return PaginatedOpenDataFoodListFromJSONTyped(json, false);
}
export function PaginatedOpenDataFoodListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PaginatedOpenDataFoodList {
if (json == null) {
return json;
}
return {
'count': json['count'],
'next': json['next'] == null ? undefined : json['next'],
'previous': json['previous'] == null ? undefined : json['previous'],
'results': ((json['results'] as Array<any>).map(OpenDataFoodFromJSON)),
'timestamp': json['timestamp'] == null ? undefined : (new Date(json['timestamp'])),
};
}
export function PaginatedOpenDataFoodListToJSON(value?: PaginatedOpenDataFoodList | null): any {
if (value == null) {
return value;
}
return {
'count': value['count'],
'next': value['next'],
'previous': value['previous'],
'results': ((value['results'] as Array<any>).map(OpenDataFoodToJSON)),
'timestamp': value['timestamp'] == null ? undefined : ((value['timestamp']).toISOString()),
};
}

View File

@@ -0,0 +1,101 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { OpenDataProperty } from './OpenDataProperty';
import {
OpenDataPropertyFromJSON,
OpenDataPropertyFromJSONTyped,
OpenDataPropertyToJSON,
} from './OpenDataProperty';
/**
*
* @export
* @interface PaginatedOpenDataPropertyList
*/
export interface PaginatedOpenDataPropertyList {
/**
*
* @type {number}
* @memberof PaginatedOpenDataPropertyList
*/
count: number;
/**
*
* @type {string}
* @memberof PaginatedOpenDataPropertyList
*/
next?: string;
/**
*
* @type {string}
* @memberof PaginatedOpenDataPropertyList
*/
previous?: string;
/**
*
* @type {Array<OpenDataProperty>}
* @memberof PaginatedOpenDataPropertyList
*/
results: Array<OpenDataProperty>;
/**
*
* @type {Date}
* @memberof PaginatedOpenDataPropertyList
*/
timestamp?: Date;
}
/**
* Check if a given object implements the PaginatedOpenDataPropertyList interface.
*/
export function instanceOfPaginatedOpenDataPropertyList(value: object): value is PaginatedOpenDataPropertyList {
if (!('count' in value) || value['count'] === undefined) return false;
if (!('results' in value) || value['results'] === undefined) return false;
return true;
}
export function PaginatedOpenDataPropertyListFromJSON(json: any): PaginatedOpenDataPropertyList {
return PaginatedOpenDataPropertyListFromJSONTyped(json, false);
}
export function PaginatedOpenDataPropertyListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PaginatedOpenDataPropertyList {
if (json == null) {
return json;
}
return {
'count': json['count'],
'next': json['next'] == null ? undefined : json['next'],
'previous': json['previous'] == null ? undefined : json['previous'],
'results': ((json['results'] as Array<any>).map(OpenDataPropertyFromJSON)),
'timestamp': json['timestamp'] == null ? undefined : (new Date(json['timestamp'])),
};
}
export function PaginatedOpenDataPropertyListToJSON(value?: PaginatedOpenDataPropertyList | null): any {
if (value == null) {
return value;
}
return {
'count': value['count'],
'next': value['next'],
'previous': value['previous'],
'results': ((value['results'] as Array<any>).map(OpenDataPropertyToJSON)),
'timestamp': value['timestamp'] == null ? undefined : ((value['timestamp']).toISOString()),
};
}

View File

@@ -0,0 +1,101 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { OpenDataStore } from './OpenDataStore';
import {
OpenDataStoreFromJSON,
OpenDataStoreFromJSONTyped,
OpenDataStoreToJSON,
} from './OpenDataStore';
/**
*
* @export
* @interface PaginatedOpenDataStoreList
*/
export interface PaginatedOpenDataStoreList {
/**
*
* @type {number}
* @memberof PaginatedOpenDataStoreList
*/
count: number;
/**
*
* @type {string}
* @memberof PaginatedOpenDataStoreList
*/
next?: string;
/**
*
* @type {string}
* @memberof PaginatedOpenDataStoreList
*/
previous?: string;
/**
*
* @type {Array<OpenDataStore>}
* @memberof PaginatedOpenDataStoreList
*/
results: Array<OpenDataStore>;
/**
*
* @type {Date}
* @memberof PaginatedOpenDataStoreList
*/
timestamp?: Date;
}
/**
* Check if a given object implements the PaginatedOpenDataStoreList interface.
*/
export function instanceOfPaginatedOpenDataStoreList(value: object): value is PaginatedOpenDataStoreList {
if (!('count' in value) || value['count'] === undefined) return false;
if (!('results' in value) || value['results'] === undefined) return false;
return true;
}
export function PaginatedOpenDataStoreListFromJSON(json: any): PaginatedOpenDataStoreList {
return PaginatedOpenDataStoreListFromJSONTyped(json, false);
}
export function PaginatedOpenDataStoreListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PaginatedOpenDataStoreList {
if (json == null) {
return json;
}
return {
'count': json['count'],
'next': json['next'] == null ? undefined : json['next'],
'previous': json['previous'] == null ? undefined : json['previous'],
'results': ((json['results'] as Array<any>).map(OpenDataStoreFromJSON)),
'timestamp': json['timestamp'] == null ? undefined : (new Date(json['timestamp'])),
};
}
export function PaginatedOpenDataStoreListToJSON(value?: PaginatedOpenDataStoreList | null): any {
if (value == null) {
return value;
}
return {
'count': value['count'],
'next': value['next'],
'previous': value['previous'],
'results': ((value['results'] as Array<any>).map(OpenDataStoreToJSON)),
'timestamp': value['timestamp'] == null ? undefined : ((value['timestamp']).toISOString()),
};
}

View File

@@ -0,0 +1,101 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { OpenDataUnit } from './OpenDataUnit';
import {
OpenDataUnitFromJSON,
OpenDataUnitFromJSONTyped,
OpenDataUnitToJSON,
} from './OpenDataUnit';
/**
*
* @export
* @interface PaginatedOpenDataUnitList
*/
export interface PaginatedOpenDataUnitList {
/**
*
* @type {number}
* @memberof PaginatedOpenDataUnitList
*/
count: number;
/**
*
* @type {string}
* @memberof PaginatedOpenDataUnitList
*/
next?: string;
/**
*
* @type {string}
* @memberof PaginatedOpenDataUnitList
*/
previous?: string;
/**
*
* @type {Array<OpenDataUnit>}
* @memberof PaginatedOpenDataUnitList
*/
results: Array<OpenDataUnit>;
/**
*
* @type {Date}
* @memberof PaginatedOpenDataUnitList
*/
timestamp?: Date;
}
/**
* Check if a given object implements the PaginatedOpenDataUnitList interface.
*/
export function instanceOfPaginatedOpenDataUnitList(value: object): value is PaginatedOpenDataUnitList {
if (!('count' in value) || value['count'] === undefined) return false;
if (!('results' in value) || value['results'] === undefined) return false;
return true;
}
export function PaginatedOpenDataUnitListFromJSON(json: any): PaginatedOpenDataUnitList {
return PaginatedOpenDataUnitListFromJSONTyped(json, false);
}
export function PaginatedOpenDataUnitListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PaginatedOpenDataUnitList {
if (json == null) {
return json;
}
return {
'count': json['count'],
'next': json['next'] == null ? undefined : json['next'],
'previous': json['previous'] == null ? undefined : json['previous'],
'results': ((json['results'] as Array<any>).map(OpenDataUnitFromJSON)),
'timestamp': json['timestamp'] == null ? undefined : (new Date(json['timestamp'])),
};
}
export function PaginatedOpenDataUnitListToJSON(value?: PaginatedOpenDataUnitList | null): any {
if (value == null) {
return value;
}
return {
'count': value['count'],
'next': value['next'],
'previous': value['previous'],
'results': ((value['results'] as Array<any>).map(OpenDataUnitToJSON)),
'timestamp': value['timestamp'] == null ? undefined : ((value['timestamp']).toISOString()),
};
}

View File

@@ -0,0 +1,101 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { OpenDataVersion } from './OpenDataVersion';
import {
OpenDataVersionFromJSON,
OpenDataVersionFromJSONTyped,
OpenDataVersionToJSON,
} from './OpenDataVersion';
/**
*
* @export
* @interface PaginatedOpenDataVersionList
*/
export interface PaginatedOpenDataVersionList {
/**
*
* @type {number}
* @memberof PaginatedOpenDataVersionList
*/
count: number;
/**
*
* @type {string}
* @memberof PaginatedOpenDataVersionList
*/
next?: string;
/**
*
* @type {string}
* @memberof PaginatedOpenDataVersionList
*/
previous?: string;
/**
*
* @type {Array<OpenDataVersion>}
* @memberof PaginatedOpenDataVersionList
*/
results: Array<OpenDataVersion>;
/**
*
* @type {Date}
* @memberof PaginatedOpenDataVersionList
*/
timestamp?: Date;
}
/**
* Check if a given object implements the PaginatedOpenDataVersionList interface.
*/
export function instanceOfPaginatedOpenDataVersionList(value: object): value is PaginatedOpenDataVersionList {
if (!('count' in value) || value['count'] === undefined) return false;
if (!('results' in value) || value['results'] === undefined) return false;
return true;
}
export function PaginatedOpenDataVersionListFromJSON(json: any): PaginatedOpenDataVersionList {
return PaginatedOpenDataVersionListFromJSONTyped(json, false);
}
export function PaginatedOpenDataVersionListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PaginatedOpenDataVersionList {
if (json == null) {
return json;
}
return {
'count': json['count'],
'next': json['next'] == null ? undefined : json['next'],
'previous': json['previous'] == null ? undefined : json['previous'],
'results': ((json['results'] as Array<any>).map(OpenDataVersionFromJSON)),
'timestamp': json['timestamp'] == null ? undefined : (new Date(json['timestamp'])),
};
}
export function PaginatedOpenDataVersionListToJSON(value?: PaginatedOpenDataVersionList | null): any {
if (value == null) {
return value;
}
return {
'count': value['count'],
'next': value['next'],
'previous': value['previous'],
'results': ((value['results'] as Array<any>).map(OpenDataVersionToJSON)),
'timestamp': value['timestamp'] == null ? undefined : ((value['timestamp']).toISOString()),
};
}

View File

@@ -164,10 +164,10 @@ export interface PatchedOpenDataFood {
propertiesSource?: string;
/**
*
* @type {string}
* @type {number}
* @memberof PatchedOpenDataFood
*/
fdcId?: string;
fdcId?: number;
/**
*
* @type {string}

View File

@@ -62,6 +62,13 @@ export * from './PaginatedInviteLinkList';
export * from './PaginatedKeywordList';
export * from './PaginatedMealPlanList';
export * from './PaginatedMealTypeList';
export * from './PaginatedOpenDataCategoryList';
export * from './PaginatedOpenDataConversionList';
export * from './PaginatedOpenDataFoodList';
export * from './PaginatedOpenDataPropertyList';
export * from './PaginatedOpenDataStoreList';
export * from './PaginatedOpenDataUnitList';
export * from './PaginatedOpenDataVersionList';
export * from './PaginatedPropertyList';
export * from './PaginatedPropertyTypeList';
export * from './PaginatedRecipeBookEntryList';

View File

@@ -1,9 +1,5 @@
<template>
<v-container>
<v-breadcrumbs density="compact">
<v-breadcrumbs-item :to="{name: 'DatabasePage'}">{{ $t('Database') }}</v-breadcrumbs-item>
</v-breadcrumbs>
<v-row>
<v-col>
<v-card prepend-icon="fa-solid fa-folder-tree" :title="$t('Database')">

View File

@@ -1,26 +0,0 @@
<template>
<v-container>
Welcome to the OpenData Plugin in Tandoor 2
<model-select model="OpenDataFood" allow-create></model-select>
</v-container>
</template>
<script setup lang="ts">
import {onMounted, ref} from "vue";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
onMounted(() => {
})
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,70 @@
<template>
<model-editor-base
:loading="loading"
:dialog="dialog"
@save="saveObject"
@delete="deleteObject"
@close="emit('close'); editingObjChanged = false"
:is-update="isUpdate()"
:is-changed="editingObjChanged"
:model-class="modelClass"
:object-name="editingObjName()">
<v-card-text>
<v-form :disabled="loading">
<model-select model="OpenDataVersion" v-model="editingObj.version"></model-select>
<v-text-field :label="$t('Open_Data_Slug')" v-model="editingObj.slug"></v-text-field>
<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-textarea :label="$t('Comment')" v-model="editingObj.comment"></v-textarea>
</v-form>
</v-card-text>
</model-editor-base>
</template>
<script setup lang="ts">
import {onMounted, PropType, watch} from "vue";
import {OpenDataCategory, OpenDataFood} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
const props = defineProps({
item: {type: {} as PropType<OpenDataCategory>, required: false, default: null},
itemId: {type: [Number, String], required: false, default: undefined},
itemDefaults: {type: {} as PropType<OpenDataCategory>, required: false, default: {} as OpenDataCategory},
dialog: {type: Boolean, default: false}
})
const emit = defineEmits(['create', 'save', 'delete', 'close', 'changedState'])
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<OpenDataCategory>('OpenDataCategory', emit)
/**
* watch prop changes and re-initialize editor
* required to embed editor directly into pages and be able to change item from the outside
*/
watch([() => props.item, () => props.itemId], () => {
initializeEditor()
})
// object specific data (for selects/display)
onMounted(() => {
initializeEditor()
})
/**
* component specific state setup logic
*/
function initializeEditor(){
setupState(props.item, props.itemId, {itemDefaults: props.itemDefaults})
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,100 @@
<template>
<model-editor-base
:loading="loading"
:dialog="dialog"
@save="saveObject"
@delete="deleteObject"
@close="emit('close'); editingObjChanged = false"
:is-update="isUpdate()"
:is-changed="editingObjChanged"
:model-class="modelClass"
:object-name="editingObjName()">
<v-card-text>
<v-form :disabled="loading">
<v-row>
<v-col>
<model-select model="OpenDataVersion" v-model="editingObj.version"></model-select>
<v-text-field :label="$t('Open_Data_Slug')" v-model="editingObj.slug"></v-text-field>
<model-select model="OpenDataFood" v-model="editingObj.food"></model-select>
</v-col>
</v-row>
<v-row>
<v-col md="6">
<v-number-input :label="$t('Amount')" :step="10" v-model="editingObj.baseAmount" control-variant="stacked" :precision="3"></v-number-input>
</v-col>
<v-col md="6">
<model-select v-model="editingObj.baseUnit" model="OpenDataUnit"></model-select>
</v-col>
</v-row>
<v-row class="mt-0">
<v-col class="text-center">
<v-icon icon="fa-solid fa-arrows-up-down"></v-icon>
</v-col>
</v-row>
<v-row>
<v-col md="6">
<v-number-input :label="$t('Amount')" :step="10" v-model="editingObj.convertedAmount" control-variant="stacked" :precision="3"></v-number-input>
</v-col>
<v-col md="6">
<model-select v-model="editingObj.convertedUnit" model="OpenDataUnit"></model-select>
</v-col>
</v-row>
<v-row>
<v-col>
<v-textarea :label="$t('Source')" v-model="editingObj.source" rows="1" auto-grow></v-textarea>
<v-textarea :label="$t('Comment')" v-model="editingObj.comment"></v-textarea>
</v-col>
</v-row>
</v-form>
</v-card-text>
</model-editor-base>
</template>
<script setup lang="ts">
import {onMounted, PropType, watch} from "vue";
import {OpenDataConversion, OpenDataFood} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
const props = defineProps({
item: {type: {} as PropType<OpenDataConversion>, required: false, default: null},
itemId: {type: [Number, String], required: false, default: undefined},
itemDefaults: {type: {} as PropType<OpenDataConversion>, required: false, default: {} as OpenDataConversion},
dialog: {type: Boolean, default: false}
})
const emit = defineEmits(['create', 'save', 'delete', 'close', 'changedState'])
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<OpenDataConversion>('OpenDataConversion', emit)
/**
* watch prop changes and re-initialize editor
* required to embed editor directly into pages and be able to change item from the outside
*/
watch([() => props.item, () => props.itemId], () => {
initializeEditor()
})
// object specific data (for selects/display)
onMounted(() => {
initializeEditor()
})
/**
* component specific state setup logic
*/
function initializeEditor() {
setupState(props.item, props.itemId, {itemDefaults: props.itemDefaults})
}
</script>
<style scoped>
</style>

View File

@@ -9,24 +9,83 @@
:is-changed="editingObjChanged"
:model-class="modelClass"
:object-name="editingObjName()">
<v-card-text>
<v-form :disabled="loading">
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
</v-form>
<v-card-text class="pa-0">
<v-tabs v-model="tab" :disabled="loading" grow>
<v-tab value="food">{{ $t('Food') }}</v-tab>
<v-tab value="properties">{{ $t('Properties') }}</v-tab>
</v-tabs>
</v-card-text>
<v-card-text>
<v-tabs-window v-model="tab">
<v-tabs-window-item value="food">
<v-form :disabled="loading">
<v-number-input :label="$t('FDC_ID')" v-model="editingObj.fdcId" :precision="0" control-variant="hidden" clearable>
<template #append-inner>
<v-btn icon="$search" size="small" density="compact" variant="plain" v-if="editingObj.fdcId == undefined"
@click="fdcDialog = true"></v-btn>
<v-btn @click="updateFoodFdcData()" icon="fa-solid fa-arrows-rotate" size="small" density="compact" variant="plain"
v-if="editingObj.fdcId"></v-btn>
<v-btn @click="openFdcPage(editingObj.fdcId)" :href="`https://fdc.nal.usda.gov/food-details/${editingObj.fdcId}/nutrients`" target="_blank"
icon="fa-solid fa-arrow-up-right-from-square"
size="small" variant="plain" v-if="editingObj.fdcId"></v-btn>
</template>
</v-number-input>
<model-select model="OpenDataVersion" v-model="editingObj.version"></model-select>
<v-text-field :label="$t('Open_Data_Slug')" v-model="editingObj.slug"></v-text-field>
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
<v-text-field :label="$t('Plural')" v-model="editingObj.pluralName"></v-text-field>
<model-select model="OpenDataCategory" v-model="editingObj.storeCategory"></model-select>
<v-textarea :label="$t('Comment')" v-model="editingObj.comment"></v-textarea>
</v-form>
</v-tabs-window-item>
<v-tabs-window-item value="properties">
<v-form :disabled="loading" class="mt-5">
<v-number-input :label="$t('Properties_Food_Amount')" v-model="editingObj.propertiesFoodAmount" :precision="2"></v-number-input>
<model-select v-model="editingObj.propertiesFoodUnit" model="OpenDataUnit"></model-select>
<v-row dense v-for="p in editingObj.properties">
<v-col>
<model-select model="OpenDataProperty" v-model="p.property" hide-details></model-select>
</v-col>
<v-col>
<v-number-input v-model="p.propertyAmount" hide-details>
<template #append>
<v-btn icon="$delete" color="delete" @click="editingObj.properties?.splice(editingObj.properties?.indexOf(p),1)"></v-btn>
</template>
</v-number-input>
</v-col>
</v-row>
<v-btn prepend-icon="$create" color="create" class="mt-2 mb-2" @click="editingObj.properties?.push({property: null, propertyAmount: 0})">{{$t('Add')}}</v-btn>
<v-textarea :label="$t('Source')" v-model="editingObj.propertiesSource" rows="1" auto-grow></v-textarea>
</v-form>
</v-tabs-window-item>
</v-tabs-window>
</v-card-text>
<fdc-search-dialog v-model="fdcDialog"
@selected="(fdcId:number) => {editingObj.fdcId = fdcId;}"></fdc-search-dialog>
</model-editor-base>
</template>
<script setup lang="ts">
import {onMounted, PropType} from "vue";
import {Keyword, OpenDataFood} from "@/openapi";
import {onMounted, PropType, ref, watch} from "vue";
import {ApiApi, OpenDataFood} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
import {openFdcPage} from "@/utils/fdc.ts";
import PropertiesEditor from "@/components/inputs/PropertiesEditor.vue";
import FdcSearchDialog from "@/components/dialogs/FdcSearchDialog.vue";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore.ts";
const props = defineProps({
item: {type: {} as PropType<OpenDataFood>, required: false, default: null},
@@ -38,12 +97,51 @@ const props = defineProps({
const emit = defineEmits(['create', 'save', 'delete', 'close', 'changedState'])
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<OpenDataFood>('OpenDataFood', emit)
/**
* watch prop changes and re-initialize editor
* required to embed editor directly into pages and be able to change item from the outside
*/
watch([() => props.item, () => props.itemId], () => {
initializeEditor()
})
// object specific data (for selects/display)
const tab = ref("food")
const fdcDialog = ref(false)
onMounted(() => {
setupState(props.item, props.itemId, {itemDefaults: props.itemDefaults})
initializeEditor()
})
/**
* component specific state setup logic
*/
function initializeEditor() {
setupState(props.item, props.itemId, {itemDefaults: props.itemDefaults})
}
/**
* Update the food FDC data on the server and update the editing object
*/
function updateFoodFdcData() {
let api = new ApiApi()
if (editingObj.value.fdcId) {
saveObject().then(() => {
loading.value = true
api.apiOpenDataFoodFdcCreate({openDataFood: editingObj.value, id: editingObj.value.id}).then(r => {
editingObj.value = r
}).catch(err => {
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
}).finally(() => {
loading.value = false
editingObjChanged.value = false
})
})
}
}
</script>
<style scoped>

View File

@@ -0,0 +1,72 @@
<template>
<model-editor-base
:loading="loading"
:dialog="dialog"
@save="saveObject"
@delete="deleteObject"
@close="emit('close'); editingObjChanged = false"
:is-update="isUpdate()"
:is-changed="editingObjChanged"
:model-class="modelClass"
:object-name="editingObjName()">
<v-card-text>
<v-form :disabled="loading">
<model-select model="OpenDataVersion" v-model="editingObj.version"></model-select>
<v-text-field :label="$t('Open_Data_Slug')" v-model="editingObj.slug"></v-text-field>
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
<v-text-field :label="$t('Unit')" v-model="editingObj.unit"></v-text-field>
<v-autocomplete :label="$t('FDC_ID')" :hint="$t('property_type_fdc_hint')" v-model="editingObj.fdcId" :items="FDC_PROPERTY_TYPES" item-title="text"></v-autocomplete>
<v-textarea :label="$t('Comment')" v-model="editingObj.comment"></v-textarea>
</v-form>
</v-card-text>
</model-editor-base>
</template>
<script setup lang="ts">
import {onMounted, PropType, watch} from "vue";
import {OpenDataFood, OpenDataProperty} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
import {FDC_PROPERTY_TYPES} from "@/utils/fdc.ts";
const props = defineProps({
item: {type: {} as PropType<OpenDataProperty>, required: false, default: null},
itemId: {type: [Number, String], required: false, default: undefined},
itemDefaults: {type: {} as PropType<OpenDataProperty>, required: false, default: {} as OpenDataProperty},
dialog: {type: Boolean, default: false}
})
const emit = defineEmits(['create', 'save', 'delete', 'close', 'changedState'])
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<OpenDataProperty>('OpenDataProperty', emit)
/**
* watch prop changes and re-initialize editor
* required to embed editor directly into pages and be able to change item from the outside
*/
watch([() => props.item, () => props.itemId], () => {
initializeEditor()
})
// object specific data (for selects/display)
onMounted(() => {
initializeEditor()
})
/**
* component specific state setup logic
*/
function initializeEditor(){
setupState(props.item, props.itemId, {itemDefaults: props.itemDefaults})
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,82 @@
<template>
<model-editor-base
:loading="loading"
:dialog="dialog"
@save="saveObject"
@delete="deleteObject"
@close="emit('close'); editingObjChanged = false"
:is-update="isUpdate()"
:is-changed="editingObjChanged"
:model-class="modelClass"
:object-name="editingObjName()">
<v-card-text>
<v-form :disabled="loading">
<model-select model="OpenDataVersion" v-model="editingObj.version"></model-select>
<v-text-field :label="$t('Open_Data_Slug')" v-model="editingObj.slug"></v-text-field>
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
<v-text-field :label="$t('Plural')" v-model="editingObj.pluralName"></v-text-field>
<base-unit-select v-model="editingObj.baseUnit"></base-unit-select>
<v-select :label="$t('Type')" :items="UNIT_TYPES" v-model="editingObj.type"></v-select>
<v-textarea :label="$t('Comment')" v-model="editingObj.comment"></v-textarea>
</v-form>
</v-card-text>
</model-editor-base>
</template>
<script setup lang="ts">
import {onMounted, PropType, watch} from "vue";
import {OpenDataFood, OpenDataUnit} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
import BaseUnitSelect from "@/components/inputs/BaseUnitSelect.vue";
import {useI18n} from "vue-i18n";
const props = defineProps({
item: {type: {} as PropType<OpenDataUnit>, required: false, default: null},
itemId: {type: [Number, String], required: false, default: undefined},
itemDefaults: {type: {} as PropType<OpenDataUnit>, required: false, default: {} as OpenDataUnit},
dialog: {type: Boolean, default: false}
})
const emit = defineEmits(['create', 'save', 'delete', 'close', 'changedState'])
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<OpenDataUnit>('OpenDataUnit', emit)
/**
* watch prop changes and re-initialize editor
* required to embed editor directly into pages and be able to change item from the outside
*/
watch([() => props.item, () => props.itemId], () => {
initializeEditor()
})
// object specific data (for selects/display)
const {t} = useI18n()
const UNIT_TYPES = [
{value: "WEIGHT", title: t("Weight")},
{value: "VOLUME", title: t("Volume")},
{value: "OTHER", title: t("Other")},
]
onMounted(() => {
initializeEditor()
})
/**
* component specific state setup logic
*/
function initializeEditor(){
setupState(props.item, props.itemId, {itemDefaults: props.itemDefaults})
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,69 @@
<template>
<model-editor-base
:loading="loading"
:dialog="dialog"
@save="saveObject"
@delete="deleteObject"
@close="emit('close'); editingObjChanged = false"
:is-update="isUpdate()"
:is-changed="editingObjChanged"
:model-class="modelClass"
:object-name="editingObjName()">
<v-card-text>
<v-form :disabled="loading">
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
<v-text-field :label="$t('Code')" v-model="editingObj.code"></v-text-field>
<v-textarea :label="$t('Comment')" v-model="editingObj.comment"></v-textarea>
</v-form>
</v-card-text>
</model-editor-base>
</template>
<script setup lang="ts">
import {onMounted, PropType, watch} from "vue";
import {OpenDataFood, OpenDataVersion} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
const props = defineProps({
item: {type: {} as PropType<OpenDataVersion>, required: false, default: null},
itemId: {type: [Number, String], required: false, default: undefined},
itemDefaults: {type: {} as PropType<OpenDataVersion>, required: false, default: {} as OpenDataVersion},
dialog: {type: Boolean, default: false}
})
const emit = defineEmits(['create', 'save', 'delete', 'close', 'changedState'])
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<OpenDataVersion>('OpenDataVersion', emit)
/**
* watch prop changes and re-initialize editor
* required to embed editor directly into pages and be able to change item from the outside
*/
watch([() => props.item, () => props.itemId], () => {
initializeEditor()
})
// object specific data (for selects/display)
onMounted(() => {
initializeEditor()
})
/**
* component specific state setup logic
*/
function initializeEditor(){
setupState(props.item, props.itemId, {itemDefaults: props.itemDefaults})
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,5 @@
{
"OpenData": "Open Data",
"OpenDataHelp": "Das Tandoor Open Data Projekt soll arbeitet an einem gemeinsamen Datensatz von Lebensmitteln, Einheiten, Umrechungen und mehr um den Start in Tandoor zu erleichtern.",
"OpenDataVersionHelp": ""
}

View File

@@ -0,0 +1,5 @@
{
"OpenData": "Open Data",
"OpenDataHelp": "The Tandoor Open Data project aims to get you started with tandoor faster and easier by providing a vast dataset of community contributed objects ranging from foods to units, stores and conversions.",
"OpenDataVersionHelp": "Every Open Data Object has a Version. Versions are used to create specific variants of the Open Data Dataset. The base version contains data valid around the world and is always in english. Other versions should be in their repective languages. "
}

View File

@@ -0,0 +1,117 @@
<template>
<v-container>
<v-row>
<v-col>
<v-card prepend-icon="fa-solid fa-folder-tree" :title="$t('OpenData')">
<v-card-text>
{{ $t('OpenDataHelp') }}
</v-card-text>
<v-card-actions>
<v-btn href="https://github.com/TandoorRecipes/open-tandoor-data" target="_blank">GitHub</v-btn>
<v-btn href="https://discord.gg/RhzBrfWgtp" target="_blank">Discord</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
<v-row>
<v-col>
<h2>Data</h2>
</v-col>
</v-row>
<v-row dense>
<database-model-col model="OpenDataFood"></database-model-col>
<database-model-col model="OpenDataUnit"></database-model-col>
<database-model-col model="OpenDataConversion"></database-model-col>
<database-model-col model="OpenDataCategory"></database-model-col>
<database-model-col model="OpenDataProperty"></database-model-col>
<database-model-col model="OpenDataVersion"></database-model-col>
</v-row>
<v-row>
<v-col>
<h2>{{ $t('Help') }}</h2>
</v-col>
</v-row>
<v-row>
<v-col>
<v-card>
<v-card-title>Quickstart</v-card-title>
<v-card-text>
<ul>
<li>Create and Edit data as you are used to with tandoor</li>
<li>Many foods can be found in the <a href="https://fdc.nal.usda.gov/index.html" target="_blank" rel="noreferrer nofollow">FDC Database</a>. Copy the
FDC ID to quickly import it in the
food editor. If appropriate for the food create a conversion (some foods will come with a measurement comment).
</li>
<li>You can only edit foods, stores and conversions you created yourself, ask on discord to get <b>verified</b> so you can edit everything.</li>
<li>If unsure about something ask others for their opinion on discord. Also take a look at this <a
href="https://github.com/TandoorRecipes/open-tandoor-data/wiki/Data-Considerations-and-Standards" target="_blank"> collection of examples</a>.
</li>
<li>Use the <code>comment</code> field in each object to add notes for other people reading or contributing.</li>
</ul>
</v-card-text>
</v-card>
<v-card class="mt-2">
<v-card-title>
Versions
</v-card-title>
<v-card-text>
<ul>
<li>Each object has a version</li>
<li>The <code>base</code> version contains data valid around the world and is always in english</li>
<li>All objects of specific versions are in their respective language</li>
</ul>
</v-card-text>
</v-card>
<v-card class="mt-2">
<v-card-title>
Translations
</v-card-title>
<v-card-text>
Data for a select language is only available for translated entries.
To help translate head over to the Tandoor Translation Page <a href="https://translate.tandoor.dev/projects/tandoor-open-data/" target="_blank">here</a>.
</v-card-text>
</v-card>
</v-col>
</v-row>
<v-row>
<v-col>
<h2>Leaderboards</h2>
</v-col>
</v-row>
<v-row>
<v-col>
WIP
</v-col>
</v-row>
</v-container>
</template>
<script setup lang="ts">
import {onMounted, ref} from "vue";
import DatabaseModelCol from "@/components/display/DatabaseModelCol.vue";
onMounted(() => {
})
</script>
<style scoped>
/**
vuetify removes all margins and paddings which break layout, re-add them by reverting vuetify change
*/
p, ol, ul, li {
padding: revert;
margin: revert;
}
</style>

View File

@@ -1,17 +1,102 @@
import {TandoorPlugin} from '@/types/Plugins.ts'
import {Model, registerModel} from "@/types/Models.ts";
import {defineAsyncComponent} from "vue";
import {VListItem} from "vuetify/components";
export const plugin: TandoorPlugin = {
name: 'Open Data Plugin',
basePath: 'open_data_plugin',
defaultLocale: import(`@/plugins/open_data_plugin/locales/en.json`),
localeFiles: import.meta.glob('@/plugins/open_data_plugin/locales/*.json'),
routes: [
{path: '/open-data/', component: () => import("@/plugins/open_data_plugin/OpenDataPage.vue"), name: 'OpenDataPage'},
]
{path: '/open-data/', component: () => import("@/plugins/open_data_plugin/pages/OpenDataPage.vue"), name: 'OpenDataPage'},
],
navigationDrawer: [],
bottomNavigation: [],
userNavigation: [
{component: VListItem, prependIcon: 'fa-solid fa-folder-tree', title: 'OpenData', to: {name: 'OpenDataPage', params: {}}},
],
} as TandoorPlugin
// define models below
const TOpenDataVersion = {
name: 'OpenDataVersion',
localizationKey: 'Version',
localizationKeyDescription: 'OpenDataVersionHelp',
icon: 'fa-solid fa-hashtag',
editorComponent: defineAsyncComponent(() => import(`@/plugins/open_data_plugin/components/model_editors/OpenDataVersionEditor.vue`)),
isPaginated: true,
isMerge: false,
toStringKeys: ['name'],
tableHeaders: [
{title: 'Name', key: 'name'},
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
registerModel(TOpenDataVersion)
const TOpenDataUnit = {
name: 'OpenDataUnit',
localizationKey: 'Unit',
localizationKeyDescription: 'UnitHelp',
icon: 'fa-solid fa-scale-balanced',
editorComponent: defineAsyncComponent(() => import(`@/plugins/open_data_plugin/components/model_editors/OpenDataUnitEditor.vue`)),
isPaginated: true,
isMerge: false,
toStringKeys: ['name'],
tableHeaders: [
{title: 'Name', key: 'name'},
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
registerModel(TOpenDataUnit)
const TOpenDataCategory = {
name: 'OpenDataCategory',
localizationKey: 'Category',
localizationKeyDescription: 'SupermarketCategoryHelp',
icon: 'fa-solid fa-boxes-stacked',
editorComponent: defineAsyncComponent(() => import(`@/plugins/open_data_plugin/components/model_editors/OpenDataCategoryEditor.vue`)),
isPaginated: true,
isMerge: false,
toStringKeys: ['name'],
tableHeaders: [
{title: 'Name', key: 'name'},
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
registerModel(TOpenDataCategory)
const TOpenDataProperty = {
name: 'OpenDataProperty',
localizationKey: 'Property',
localizationKeyDescription: 'PropertyTypeHelp',
icon: 'fa-solid fa-database',
editorComponent: defineAsyncComponent(() => import(`@/plugins/open_data_plugin/components/model_editors/OpenDataPropertyEditor.vue`)),
isPaginated: true,
isMerge: false,
toStringKeys: ['name'],
tableHeaders: [
{title: 'Name', key: 'name'},
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
registerModel(TOpenDataProperty)
const TOpenDataFood = {
name: 'OpenDataFood',
localizationKey: 'Food',
@@ -20,13 +105,39 @@ const TOpenDataFood = {
editorComponent: defineAsyncComponent(() => import(`@/plugins/open_data_plugin/components/model_editors/OpenDataFoodEditor.vue`)),
isPaginated: false,
isPaginated: true,
isMerge: false,
toStringKeys: ['name'],
tableHeaders: [
{title: 'Name', key: 'name'},
{title: 'Category', key: 'storeCategory.name'},
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
registerModel(TOpenDataFood)
registerModel(TOpenDataFood)
const TOpenDataConversion = {
name: 'OpenDataConversion',
localizationKey: 'UnitConversion',
localizationKeyDescription: 'UnitConversionHelp',
icon: 'fa-solid fa-exchange-alt',
editorComponent: defineAsyncComponent(() => import(`@/plugins/open_data_plugin/components/model_editors/OpenDataConversionEditor.vue`)),
isPaginated: true,
isMerge: false,
toStringKeys: ['name'],
tableHeaders: [
{title: 'Food', key: 'food.name'},
{title: 'base_amount', key: 'baseAmount'},
{title: 'base_unit', key: 'baseUnit.name'},
{title: 'converted_amount', key: 'convertedAmount'},
{title: 'converted_unit', key: 'convertedUnit.name'},
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
registerModel(TOpenDataConversion)

View File

@@ -1,7 +1,19 @@
import {RouteRecordRaw} from "vue-router";
import {Model, registerModel} from "@/types/Models.ts";
export type TandoorPlugin = {
name: string,
basePath: string,
defaultLocale: any,
localeFiles: any,
routes: RouteRecordRaw[]
navigationDrawer: any[],
bottomNavigation: any[],
userNavigation: any[],
}
const pluginModules = import.meta.glob('@/plugins/*/plugin.ts', { eager: true })
export let TANDOOR_PLUGINS = [] as TandoorPlugin[]
Object.values(pluginModules).forEach(module => {
TANDOOR_PLUGINS.push(module.plugin)
})