Skip to content
Snippets Groups Projects
Commit 072d14b2 authored by Håkon Eilertsen Røskaft's avatar Håkon Eilertsen Røskaft
Browse files

Fixed a merge conflict and added some fixed to item page

parents c08110dc a6fbbc39
No related branches found
No related tags found
1 merge request!81Renting
Pipeline #179008 passed
Showing
with 488 additions and 100 deletions
image: node:16
stages:
- setup
# - setup
- test
variables:
npm_config_cache: "$CI_PROJECT_DIR/.npm"
#variables:
# npm_config_cache: "$CI_PROJECT_DIR/.npm"
# Define a hidden job to be used with extends
# Better than default to avoid activating cache for all jobs
.dependencies_cache:
cache:
key:
files:
- package-lock.json
paths:
- .npm
policy: pull
#.dependencies_cache:
# cache:
# key:
# files:
# - package-lock.json
# paths:
# - .npm
# policy: pull
setup:
stage: setup
script:
- npm ci
extends: .dependencies_cache
cache:
policy: pull-push
artifacts:
expire_in: 3 days #delete cache after 3 days to conserve space
paths:
- node_modules
#setup:
# stage: setup
# script:
# - npm ci
# extends: .dependencies_cache
# cache:
# policy: pull-push
# artifacts:
# expire_in: 3 days #delete cache after 3 days to conserve space
# paths:
# - node_modules
lint_test:
test:
stage: test
script:
- npm ci
- npm run lint
unit_test:
stage: test
script:
- npm run test:unit
#unit_test:
# stage: test
# script:
# - npm run test:unit
......@@ -8,6 +8,7 @@
"name": "frontend",
"version": "0.1.0",
"dependencies": {
"@heroicons/vue": "^1.0.6",
"@mdi/font": "5.9.55",
"@vuelidate/core": "^2.0.0-alpha.40",
"@vuelidate/validators": "^2.0.0-alpha.28",
......@@ -1707,6 +1708,14 @@
"@hapi/hoek": "^9.0.0"
}
},
"node_modules/@heroicons/vue": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@heroicons/vue/-/vue-1.0.6.tgz",
"integrity": "sha512-ng2YcCQrdoQWEFpw+ipFl2rZo8mZ56v0T5+MyfQQvNqfKChwgP6DMloZLW+rl17GEcHkE3H82UTAMKBKZr4+WA==",
"peerDependencies": {
"vue": ">= 3"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.5.0",
"dev": true,
......@@ -16108,6 +16117,12 @@
"@hapi/hoek": "^9.0.0"
}
},
"@heroicons/vue": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@heroicons/vue/-/vue-1.0.6.tgz",
"integrity": "sha512-ng2YcCQrdoQWEFpw+ipFl2rZo8mZ56v0T5+MyfQQvNqfKChwgP6DMloZLW+rl17GEcHkE3H82UTAMKBKZr4+WA==",
"requires": {}
},
"@humanwhocodes/config-array": {
"version": "0.5.0",
"dev": true,
......
......@@ -9,6 +9,7 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"@heroicons/vue": "^1.0.6",
"@mdi/font": "5.9.55",
"@vuelidate/core": "^2.0.0-alpha.40",
"@vuelidate/validators": "^2.0.0-alpha.28",
......
public/favicon.ico

15 KiB

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
......@@ -9,14 +9,18 @@
import { defineComponent } from "vue";
// Components
import NavBar from "./components/NavigationComponents/NavBar.vue";
import NavBar from "./components/BaseComponents/NavBar.vue";
export default defineComponent({
name: "App",
components: {
NavBar,
},
});
</script>
<style global>
@tailwind base;
@tailwind components;
@tailwind utilities;
</style>
src/assets/members.png

4.38 KiB

src/assets/newCommunity.png

19 KiB

<template>
<button
class="block text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
>
{{ text }}
</button>
</template>
<script>
export default {
name: "ColoredButton",
props: {
text: String,
},
};
</script>
<template>
<div class="flex items-center justify-between mx-4">
<div class="flex-1 min-w-0">
<h2
class="text-2xl font-bold leading-7 text-gray-900 sm:text-3xl sm:truncate"
>
{{ community.name }}
</h2>
<div
class="mt-1 flex flex-col sm:flex-row sm:flex-wrap sm:mt-0 sm:space-x-6"
>
<div class="mt-2 flex items-center text-sm text-gray-500">
<svg
class="flex-shrink-0 mr-1.5 h-5 w-5 text-gray-400"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z"
clip-rule="evenodd"
/>
</svg>
{{ community.location }}
</div>
</div>
</div>
<div>
<span class="hidden sm:block">
<!-- Legg dette til i button: v-if="adminStatus" -->
<svg
@click="toggle"
xmlns="http://www.w3.org/2000/svg"
class="w-9 h-9 cursor-pointer"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
<CommunityHamburger
v-if="hamburgerOpen"
class="origin-top-right absolute right-0"
:community-i-d="community.communityId"
/>
<!-- class="absolute" -->
</span>
</div>
</div>
</template>
<script>
import CommunityHamburger from "@/components/CommunityComponents/CommunityHamburger";
export default {
name: "CommunityHeader",
components: {
CommunityHamburger,
},
data() {
return {
hamburgerOpen: false,
};
},
props: {
adminStatus: Boolean,
community: {
communityId: Number,
name: String,
description: String,
visibility: Number,
location: String,
picture: String,
},
},
methods: {
toggle: function () {
if (this.hamburgerOpen) {
this.hamburgerOpen = false;
} else {
this.hamburgerOpen = true;
}
},
},
};
</script>
<template>
<!-- Main modal -->
<div
v-if="visible"
class="fixed grid place-items-center bg-gray-600 bg-opacity-50 top-0 left-0 right-0 z-50 w-full overflow-x-hidden overflow-y-auto md:inset-0 h-modal md:h-full"
>
<div class="relative w-full h-full max-w-2xl p-4 md:h-auto">
<!-- Modal content -->
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
<!-- Modal header -->
<div
class="flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600"
>
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
{{ title }}
</h3>
<button
@click="close()"
class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white"
>
<svg
class="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"
></path>
</svg>
</button>
</div>
<!-- Modal body -->
<div class="p-6 space-y-6">
<p class="text-base leading-relaxed text-gray-500 dark:text-gray-400">
{{ message }}
</p>
</div>
<!-- Modal footer -->
<div class="rounded-b border-t border-gray-200 dark:border-gray-600">
<!-- Slot: Add any html you want here -->
<slot />
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "CustomFooterModal",
props: {
visible: Boolean,
title: String,
message: String,
},
methods: {
close() {
this.$emit("close");
},
},
};
</script>
<template>
<!-- Icon button -->
<button
class="block w-fit text-white text-base bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
>
<div class="flex flex-row px-5 py-2.5 h-10">
<!-- Icon slot: Default content "Ban"-icon -->
<div class="h-6 w-6">
<slot>
<BanIcon />
</slot>
</div>
<p>{{ text }}</p>
</div>
</button>
</template>
<script>
import { BanIcon } from "@heroicons/vue/outline";
export default {
name: "IconButton",
props: {
text: String,
},
components: {
BanIcon,
},
};
</script>
<template>
<nav
class="flex items-center justify-between bg-white h-14 border-1 border-b border-gray-300 border-solid"
class="flex items-center justify-between bg-white h-14 border-1 border-b border-gray-300 border-solid sticky top-0 z-50"
>
<div class="logo">
<img
......
<template>
<!-- Main modal -->
<div
v-if="visible"
class="fixed grid place-items-center bg-gray-600 bg-opacity-50 top-0 left-0 right-0 z-50 w-full overflow-x-hidden overflow-y-auto md:inset-0 h-modal md:h-full"
>
<div class="relative w-full h-full max-w-2xl p-4 md:h-auto">
<!-- Modal content -->
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
<!-- Modal header -->
<div
class="flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600"
>
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
{{ title }}
</h3>
<button
@click="close()"
class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white"
>
<svg
class="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"
></path>
</svg>
</button>
</div>
<!-- Modal body -->
<div class="p-6 space-y-6">
<p class="text-base leading-relaxed text-gray-500 dark:text-gray-400">
{{ message }}
</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "NotificationModal",
props: {
visible: Boolean,
title: String,
message: String,
},
methods: {
close() {
this.$emit("close");
},
},
};
</script>
......@@ -47,7 +47,7 @@ export default {
calculateTime() {
//let time = this.message.from;
// Calculate time when message was sent
let date = new Date(Date.now());
let date = new Date(this.message.timestamp);
let hours = date.getHours();
let minutes = "0" + date.getMinutes();
let formattedTime = hours + ":" + minutes.substr(-2);
......
<template>
<div
id="dropdown"
class="z-10 w-44 text-base list-none bg-white rounded divide-y divide-gray-100 shadow dark:bg-gray-700"
>
<ul class="py-1">
<li id="newItem">
<router-link
to="/addNewItem"
class="block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
>Opprett Utleie</router-link
>
</li>
<li id="getMembers">
<router-link
:to="'/group/' + communityID + '/memberlist'"
class="block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
>Se Medlemmer
</router-link>
</li>
<li id="adminGroup">
<router-link
:to="'/group/' + communityID + '/memberlist'"
class="block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
>Administrer Gruppe</router-link
>
</li>
<li id="leaveGroup">
<div
class="cursor-pointer block py-2 px-4 text-sm text-red-600 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
>
Forlat Gruppe
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "CommunityHamburger",
props: {
communityID: Number,
},
};
</script>
<template>
<section class="relative w-full max-w-md px-5 py-4 mx-auto rounded-md">
<div class="mb-5 mt-5 border-b-2 border-blue-900">
<label class="text-xl font-bold">Tøyenhus borettslag</label>
</div>
<section class="w-full px-5 py-4 mx-auto rounded-md">
<CommunityHeader
:admin-status="false"
:community="community"
class="mb-5"
/>
<!-- Search field -->
<div class="relative" id="searchComponent">
<span class="absolute inset-y-0 left-0 flex items-center pl-3">
<svg class="w-5 h-5 text-gray-400" viewBox="0 0 24 24" fill="none">
......@@ -25,9 +29,17 @@
/>
</div>
<div class="absolute inset-x-0 px-6 py-3 mt-4">
<div class="grid grid-cols-2">
<ItemCard v-for="item in searchedItems" :key="item" :item="item" @click="goToItemInfoPage(item)"/>
<!-- Item cards -->
<div class="absolute inset-x-0 px-6 py-3">
<div
class="grid grid-flow-row-dense grid-cols-2 md:grid-cols-4 lg:grid-cols-5 w-full place-items-center"
>
<ItemCard
v-for="item in searchedItems"
:key="item"
:item="item"
@click="goToItemInfoPage(item.listingID)"
/>
</div>
</div>
</section>
......@@ -35,19 +47,16 @@
<script>
import ItemCard from "@/components/CommunityComponents/ItemCard";
import CommunityHeader from "@/components/BaseComponents/CommunityHeader";
import { GetCommunity, GetListingsInCommunity } from "@/utils/apiutil";
export default {
name: "SearchItemListComponent",
components: {
CommunityHeader,
ItemCard,
},
methods: {
goToItemInfoPage(item) {
this.$router.push("/itempage/" + item.listingID);
}
},
computed: {
searchedItems() {
let filteredItems = [];
......@@ -55,37 +64,51 @@ export default {
filteredItems = this.items.filter(
(p) =>
p.title.toLowerCase().includes(this.search.toLowerCase()) ||
p.adresse.toLowerCase().includes(this.search.toLowerCase()) ||
p.price === Number(this.search)
p.address.toLowerCase().includes(this.search.toLowerCase()) ||
p.pricePerDay === Number(this.search)
);
return filteredItems;
},
},
/**
* Her må det lages en metode som henter alle items (i en gruppe) fra databasen.
* De kan deretter bli pusha inn i items array, og da burde de bli displayet i lista.
* Når denne metoden er på plass kan items[] i data tømmes. Da vil alt dataen komme fra db.
*/
created() {
if (this.$store.state.user.token !== null) {
this.isLoggedIn = true;
}
},
data() {
return {
items: [
{ img: "", adresse: "Oslo", title: "Dyson", price: 1000 , listingID: 999},
{ img: "", adresse: "Trondheim", title: "Gressklipper", price: 500 },
{ img: "", adresse: "Bergen", title: "Bil", price: 500 },
],
items: [],
item: {
listingID: 0,
img: "",
adresse: "",
address: "",
title: "",
price: 0,
pricePerDay: 0,
},
search: "",
communityID: -1,
community: {},
};
},
methods: {
getCommunityFromAPI: async function () {
this.communityID = await this.$router.currentRoute.value.params
.communityID;
this.community = await GetCommunity(this.communityID);
},
getListingsOfCommunityFromAPI: async function () {
this.communityID = await this.$router.currentRoute.value.params
.communityID;
this.items = await GetListingsInCommunity(this.communityID);
},
goToItemInfoPage(item) {
this.$router.push("/itempage/" + item);
},
},
beforeMount() {
this.getCommunityFromAPI(); //To get the id of the community before mounting the view
this.getListingsOfCommunityFromAPI();
},
};
</script>
<template>
<ul>
<li v-for="(group, index) in groupList" :key="index">
<group-list-item :group="group" />
<li v-for="community in communities" :key="community">
<CommunityListItem :community="community" :member="member" />
</li>
</ul>
</template>
<script>
import GroupListItem from "@/components/CommunityComponents/CommunityListItem.vue";
import CommunityListItem from "@/components/CommunityComponents/CommunityListItem.vue";
export default {
name: "GroupList",
name: "CommunityList",
props: {
groupList: Array,
communities: Array,
member: Boolean,
},
components: {
GroupListItem,
CommunityListItem,
},
};
</script>
<template>
<CustomFooterModal
@close="this.dialogOpen = false"
:visible="dialogOpen"
:title="community.name"
:message="community.description"
>
<div class="flex justify-center p-2">
<ColoredButton
v-if="!member"
:text="'Bli med'"
@click="goToJoin(community.communityId)"
/>
<ColoredButton
v-if="member"
:text="'Gå til'"
@click="goToGroup(community.communityId)"
/>
</div>
</CustomFooterModal>
<div
@click="toggleDialog()"
class="bg-white shadow dark:bg-gray-800 select-none cursor-pointer hover:bg-gray-50 flex items-center p-4"
>
<div class="h-10 w-10 flex flex-col justify-center items-center mr-4">
<img alt="groupIMG" src="../../assets/group.png" />
<UserGroupIcon v-if="!community.image" alt="Felleskapets bilde" />
<!-- TODO: USE COMMUNITY IMAGE <img alt="Felleskapets bilde" src="@/assets/group.png" /> -->
</div>
<div class="flex-1 pl-1">
<div class="font-medium dark:text-white">
{{ group.name }}
<div class="flex-1 pl-1 overflow-hidden">
<div class="font-medium dark:text-white truncate">
{{ community.name }}
</div>
</div>
<div class="flex flex-row justify-center">
<button
@click="goToJoin(group.communityId)"
v-if="!isMember"
class="px-4 py-2 w-24 font-medium tracking-wide text-white capitalize transition-colors duration-200 transform bg-blue-600 rounded-md hover:bg-blue-500 focus:outline-none focus:ring focus:ring-blue-300 focus:ring-opacity-80"
>
Bli med
</button>
<button
v-if="isMember"
@click="goToGroup(group.communityId)"
class="px-4 py-2 w-24 font-medium tracking-wide text-white capitalize transition-colors duration-200 transform bg-blue-600 rounded-md hover:bg-blue-500 focus:outline-none focus:ring focus:ring-blue-300 focus:ring-opacity-80"
>
Gå til
</button>
<div class="flex flex-row justify-center items-center">
<LockClosedIcon
v-if="community.visibility === 0"
class="max-h-6 max-w-6 shrink m-2"
/>
</div>
</div>
</template>
<script>
import { getMyGroups } from "@/utils/apiutil";
import CustomFooterModal from "@/components/BaseComponents/CustomFooterModal.vue";
import ColoredButton from "@/components/BaseComponents/ColoredButton.vue";
import { UserGroupIcon, LockClosedIcon } from "@heroicons/vue/outline";
export default {
name: "GroupListItem",
name: "CommunityListItem",
components: {
CustomFooterModal,
ColoredButton,
UserGroupIcon,
LockClosedIcon,
},
data() {
return {
myGroups: [],
dialogOpen: false,
};
},
props: {
group: Object,
community: Object,
member: Boolean,
},
methods: {
goToGroup(id) {
......@@ -49,15 +69,9 @@ export default {
goToJoin(id) {
this.$router.push("/community/" + id + "/join");
},
async getMyGroups() {
this.myGroups = await getMyGroups();
toggleDialog() {
this.dialogOpen = !this.dialogOpen;
},
isMember(group) {
return this.myGroups.includes(group);
},
},
beforeMount() {
this.getMyGroups();
},
};
</script>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment