Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue55/vue cards overview #58

Merged
merged 3 commits into from
Jan 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions auth-server/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ tasks.withType<Test> {
}

val dockerHubRepo = "wisskirchenj/"
// On ARM64 (!) uncomment the following lines to build JVM AMD64 image, since then default paketo builder is used
tasks.named<BootBuildImage>("bootBuildImage") {
builder.set("dashaun/builder:tiny")
imageName.set(dockerHubRepo + rootProject.name + project.name + ":" + version)
Expand Down
1,132 changes: 514 additions & 618 deletions frontend/package-lock.json

Large diffs are not rendered by default.

31 changes: 15 additions & 16 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,25 @@
"lint": "eslint . --fix --ignore-path .gitignore"
},
"dependencies": {
"@mdi/font": "7.0.96",
"axios": "^1.6.5",
"axios-oauth-client": "^2.0.4",
"@mdi/font": "7.4.47",
"axios": "^1.6.7",
"roboto-fontface": "*",
"vue": "^3.2.0",
"vue-router": "^4.0.0",
"vuetify": "^3.0.0"
"vue": "^3.4.15",
"vue-router": "^4.2.5",
"vuetify": "^3.5.1"
},
"devDependencies": {
"@babel/types": "^7.21.4",
"@types/node": "^18.15.0",
"@vitejs/plugin-vue": "^4.0.0",
"@vue/eslint-config-typescript": "^11.0.0",
"@babel/types": "^7.23.9",
"@types/node": "^20.11.7",
"@vitejs/plugin-vue": "^5.0.3",
"@vue/eslint-config-typescript": "^12.0.0",
"eslint": "^8.0.0",
"eslint-plugin-vue": "^9.0.0",
"sass": "^1.69.7",
"typescript": "^5.0.0",
"eslint-plugin-vue": "^9.20.1",
"sass": "^1.70.0",
"typescript": "^5.3.3",
"unplugin-fonts": "^1.0.3",
"vite": "^4.2.0",
"vite-plugin-vuetify": "^1.0.0",
"vue-tsc": "^1.2.0"
"vite": "^5.0.12",
"vite-plugin-vuetify": "^2.0.1",
"vue-tsc": "^1.8.27"
}
}
2 changes: 1 addition & 1 deletion frontend/src/feature/cards/composables/useCardsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const useCardsService = () => {
}

const getCards = async (categoryId: string, page: number, errorResult: string = 'throw') : Promise<Card[]> => {
const url = apiUrl + 'cards?categoryId=' + categoryId + '&page=' + page;
const url = `${apiUrl}cards?categoryId=${categoryId}&page=${page}`;

try {
const response = await apiClient.get(url);
Expand Down
124 changes: 124 additions & 0 deletions frontend/src/feature/category/components/CategoryCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<template>
<v-card>
<v-card-title class="d-flex justify-space-between align-center">
<h4>{{ category.name }}</h4>
<div>
<v-btn variant="text"
@click="openCategory()"
icon="mdi-open-in-app"
size="x-large"/>
<v-btn variant="text"
icon="mdi-shape-rectangle-plus"
:disabled="!putUri"
@click="addCategory()"
size="x-large" />
<v-btn variant="text"
icon="mdi-square-edit-outline"
:disabled="!putUri"
@click="editCategory()"
size="x-large" />
<v-btn variant="text"
:disabled="!!deleteUri"
@click="deleteCategory()"
icon="mdi-trash-can-outline"
size="x-large" />
</div>
</v-card-title>

<v-card-text>
{{ category.description }}
</v-card-text>

<div class="px-4">
<v-switch
:model-value="expanded"
:label="`Show details`"
:color="`${expanded ? '#43A047' : '#EEEEEE'}`"
density="compact"
inset
@click="() => emit('update:expanded', !expanded)"
></v-switch>
</div>

<v-divider></v-divider>

<v-expand-transition>
<div v-if="expanded && !addRequested && ! editRequested">
<v-list density="compact" :lines="false">
<v-list-item :title="`🔥 Your access: ${getAccess(category)}`"></v-list-item>
<v-list-item :title="`🍔 #Cards in Category: ${category.numberOfCards}`"></v-list-item>
<v-list-item :title="`🧲 Id: ${category.id}`"></v-list-item>
</v-list>
</div>
<div v-if="addRequested || editRequested" class="d-flex justify-space-between align-center">
<v-text-field v-model="categoryName" label="Enter category name">
</v-text-field>
<v-btn border @click="performUpdate(categoryName)" variant="text">
Submit
</v-btn>
</div>
</v-expand-transition>
</v-card>
</template>

<script setup lang="ts">
import {Category} from "@/feature/category/model/category";
import {getAccess, getDeleteUri, getPutUri} from "@/feature/category/composables/useCategory";
import {useRouter} from "vue-router";
import {ref} from "vue";
import categoryService from "@/feature/category/composables/useCategoriesService";

const props = defineProps<({
category: Category,
expanded: boolean
})>();
const emit = defineEmits<{
'update:expanded': [val: boolean]
}>();

const putUri = getPutUri(props.category);
const deleteUri = getDeleteUri(props.category);
const addRequested = ref(false);
const editRequested = ref(false);
const categoryName = ref("");

const router = useRouter();
const openCategory = () => {
resetRequests();
router.push(`/display/${props.category.id}`);
};

const performUpdate = (categoryName: string) => {
if (addRequested.value) {
categoryService().postNewCategory(categoryName);
} else if (editRequested.value) {
categoryService().putCategory(props.category.id, categoryName);
}
};

const addCategory = () => {
resetRequests();
addRequested.value = true;
return `/display/${props.category.id}`;
};
const editCategory = () => {
resetRequests();
editRequested.value = true;
return `/display/${props.category.id}`;
};
const deleteCategory = () => {
resetRequests();
categoryService().deleteCategory(props.category.id);
};

const resetRequests = () => {
addRequested.value = false;
editRequested.value = false;
}
</script>

<style scoped lang="scss">
.v-card {
background-color: #f0fff0;
}
</style>
58 changes: 14 additions & 44 deletions frontend/src/feature/category/components/CategoryIterator.vue
Original file line number Diff line number Diff line change
@@ -1,60 +1,30 @@
<template>
<v-data-iterator
:items="props.categories"
item-value="name"
:items="categories"
item-value="category.name"
>
<template v-slot:default="{ items, isExpanded, toggleExpand }">
<template v-slot:default="{items}">
<v-row>
<v-col
v-for="item in items"
:key="item.raw.name"
cols="12"
sm="12"
md="6"
v-for="item in items" :key="item.raw.category.name"
cols="12" sm="12" md="6"
>
<v-card>
<v-card-title class="d-flex align-center">
<h4>{{ item.raw.name }}</h4>
</v-card-title>

<v-card-text>
{{ item.raw.description }}
</v-card-text>

<div class="px-4">
<v-switch
:model-value="isExpanded(item as any)"
:label="`Show details`"
:color="`${isExpanded(item as any) ? '#43A047' : '#EEEEEE'}`"
density="compact"
inset
@click="() => toggleExpand(item as any)"
></v-switch>
</div>

<v-divider></v-divider>

<v-expand-transition>
<div v-if="isExpanded(item as any)">
<v-list density="compact" :lines="false">
<v-list-item :title="`🔥 Your access: ${getAccess(item.raw)}`"></v-list-item>
<v-list-item :title="`🍔 #Cards in Category: ${item.raw.numberOfCards}`"></v-list-item>
<v-list-item :title="`🧲 Id: ${item.raw.id}`"></v-list-item>
</v-list>
</div>
</v-expand-transition>
</v-card>
<category-card :category="item.raw.category"
v-model:expanded="item.raw.expanded"
/>
</v-col>
</v-row>
</template>
</v-data-iterator>
</template>

<script setup lang="ts">
import {Category, getAccess} from "@/feature/category/model/category";
import {Category} from "@/feature/category/model/category";
import CategoryCard from "@/feature/category/components/CategoryCard.vue";

defineProps<({
categories: { category: Category, expanded: boolean }[]
})>();

const props = defineProps({
categories: Array as () => Category[]
})

</script>
62 changes: 59 additions & 3 deletions frontend/src/feature/category/composables/useCategoriesService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ import apiClient from '@/plugins/axios';
import {useErrorService} from "@/shared/composables/errorService";
import {Category} from "@/feature/category/model/category";
import apiUrl from "@/shared/composables/baseUrl";
import {useToastService} from "@/shared/composables/toastService";

const useCategoriesService = () => {

const getCategories = async (errorResult: string = 'throw') : Promise<Category[]> => {
const url = apiUrl + 'categories';

const url = `${apiUrl}categories`;
try {
const response = await apiClient.get(url);
if (response.status !== 200) {
Expand All @@ -22,8 +21,65 @@ const useCategoriesService = () => {
}
}

const postNewCategory = async (name: string, errorResult: string = 'throw') => {
const url = `${apiUrl}categories`;
const postData = {
name: name
};

try {
const response = await apiClient.post(url, postData);
if (response.status !== 201) {
throw new Error(`Error status code ${response.status}!`);
} else {
useToastService().showSuccess('SUCCESS', `Category ${name} successfully created!`)
}
} catch (error: any) {
errorResult === 'throw' ? useErrorService().handleAndThrow(error)
: useErrorService().handleAndNotify('custom error', 'custom message');
}
}

const putCategory = async (id: string, newName: string, errorResult: string = 'throw') => {
const url = `${apiUrl}categories/${id}`;
const putData = {
name: newName
};

try {
const response = await apiClient.put(url, putData);
if (response.status !== 200) {
throw new Error(`Error status code ${response.status}!`);
} else {
useToastService().showSuccess('SUCCESS', `Category successfully renamed to ${newName}!`)
}
} catch (error: any) {
errorResult === 'throw' ? useErrorService().handleAndThrow(error)
: useErrorService().handleAndNotify('custom error', 'custom message');
}
}

const deleteCategory = async (id: string, errorResult: string = 'throw') => {
const url = `${apiUrl}categories/${id}`;

try {
const response = await apiClient.delete(url);
if (response.status !== 200) {
throw new Error(`Error status code ${response.status}!`);
} else {
useToastService().showSuccess('SUCCESS', `Category successfully removed`)
}
} catch (error: any) {
errorResult === 'throw' ? useErrorService().handleAndThrow(error)
: useErrorService().handleAndNotify('custom error', 'custom message');
}
}

return {
getCategories,
postNewCategory,
putCategory,
deleteCategory
}
}
export default useCategoriesService;
13 changes: 13 additions & 0 deletions frontend/src/feature/category/composables/useCategory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {ActionType, Category} from "@/feature/category/model/category";

export const getAccess = (category: Category): string => {
return category.actions.map(item => item.action).join(', ');
}

export const getPutUri = (category: Category): string | undefined => {
return category.actions.filter(item => item.action === ActionType.WRITE)[0]?.uri;
}

export const getDeleteUri = (category: Category): string | undefined => {
return category.actions.filter(item => item.action === ActionType.DELETE)[0]?.uri;
}
12 changes: 7 additions & 5 deletions frontend/src/feature/category/model/category.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
export enum ActionType {
READ = 'READ',
WRITE = 'WRITE',
DELETE = 'DELETE'
}

export type Action = {
action: string,
action: ActionType,
uri: string
}

Expand All @@ -10,7 +16,3 @@ export type Category = {
numberOfCards?: number,
description?: string,
}

export const getAccess = (category: Category): string => {
return category.actions.map(item => item.action).join(', ');
}
Loading
Loading