6 Commits

16 changed files with 386 additions and 6 deletions
Split View
  1. +21
    -0
      .idea/inspectionProfiles/Project_Default.xml
  2. +6
    -0
      .idea/jsLibraryMappings.xml
  3. +6
    -0
      .idea/prettier.xml
  4. +1
    -0
      gca-admin-gurusoft-message-dashboard/.env
  5. +1
    -1
      gca-admin-gurusoft-message-dashboard/README.md
  6. +4
    -1
      gca-admin-gurusoft-message-dashboard/src/components/Dashboard.vue
  7. +114
    -0
      gca-admin-gurusoft-message-dashboard/src/components/MarkedPopUp.vue
  8. +97
    -0
      gca-admin-gurusoft-message-dashboard/src/components/SystemLinks.vue
  9. +1
    -1
      gca-admin-gurusoft-message-dashboard/src/main.js
  10. +62
    -0
      gca-admin-gurusoft-message-dashboard/src/mocks/marketPopUpMockData.json
  11. +0
    -0
      gca-admin-gurusoft-message-dashboard/src/mocks/overlayPopup.json
  12. +62
    -0
      gca-admin-gurusoft-message-dashboard/src/mocks/systemLinksMockData.json
  13. +0
    -0
      gca-admin-gurusoft-message-dashboard/src/mocks/systemMessage.json
  14. +3
    -1
      gca-admin-gurusoft-message-dashboard/src/router/index.js
  15. +2
    -2
      gca-admin-gurusoft-message-dashboard/src/utils/api.js
  16. +6
    -0
      package-lock.json

+ 21
- 0
.idea/inspectionProfiles/Project_Default.xml View File

@ -0,0 +1,21 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myValues">
<value>
<list size="7">
<item index="0" class="java.lang.String" itemvalue="nobr" />
<item index="1" class="java.lang.String" itemvalue="noembed" />
<item index="2" class="java.lang.String" itemvalue="comment" />
<item index="3" class="java.lang.String" itemvalue="noscript" />
<item index="4" class="java.lang.String" itemvalue="embed" />
<item index="5" class="java.lang.String" itemvalue="script" />
<item index="6" class="java.lang.String" itemvalue="router-link" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
</profile>
</component>

+ 6
- 0
.idea/jsLibraryMappings.xml View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="file://$PROJECT_DIR$" libraries="{bootstrap}" />
</component>
</project>

+ 6
- 0
.idea/prettier.xml View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PrettierConfiguration">
<option name="myConfigurationMode" value="AUTOMATIC" />
</component>
</project>

+ 1
- 0
gca-admin-gurusoft-message-dashboard/.env View File

@ -0,0 +1 @@
VITE_USE_MOCK=true

+ 1
- 1
gca-admin-gurusoft-message-dashboard/README.md View File

@ -1,4 +1,4 @@
# gca-admin-gurusoft-message-dashboard
npm # gca-admin-gurusoft-message-dashboard
This template should help get you started developing with Vue 3 in Vite.

+ 4
- 1
gca-admin-gurusoft-message-dashboard/src/components/Dashboard.vue View File

@ -1,6 +1,8 @@
<script setup lang="ts">
import OverlayPopup from "./OverlayPopup.vue";
import SystemMesssages from "./SystemMesssages.vue";
import SystemLinks from "@/components/SystemLinks.vue";
import MarkedPopUp from "@/components/MarkedPopUp.vue";
</script>
<style scoped>
@ -36,13 +38,14 @@ import SystemMesssages from "./SystemMesssages.vue";
<SystemMesssages/>
</div>
<div class="col border border-warning">
2 of 2
<SystemLinks/>
</div>
</div>
</div>
</div>
</div>
<OverlayPopup/>
<MarkedPopUp/>
</template>

+ 114
- 0
gca-admin-gurusoft-message-dashboard/src/components/MarkedPopUp.vue View File

@ -0,0 +1,114 @@
<script setup>
import {ref, onMounted, defineProps} from 'vue';
import popupData from '@/mocks/marketPopUpMockData.json';
const props = defineProps({
useMockedData: {
type: Boolean,
default: true
}
});
const popups = ref([]);
const currentPopup = ref(null);
const loading = ref(true);
const error = ref(null);
const showPopup = ref(false);
onMounted(async () => {
try {
if (props.useMockedData) {
// Use mock data
popups.value = popupData;
if (popups.value.length > 0) {
currentPopup.value = popups.value[4]; // Display first popup
}
loading.value = false;
} else {
// Fetch from API
const response = await fetch(`https://TODO-replace-with-API`);
if (!response.ok) {
throw new Error('Failed to fetch popup data');
}
popups.value = await response.json();
if (popups.value.length > 0) {
currentPopup.value = popups.value[0];
}
loading.value = false;
}
} catch (e) {
error.value = e.message;
loading.value = false;
}
});
const togglePopup = () => {
showPopup.value = !showPopup.value;
};
const closePopup = () => {
showPopup.value = false;
};
const handleAction = () => {
if (currentPopup.value?.url) {
window.open(currentPopup.value.url, '_blank');
}
closePopup();
};
</script>
<template>
<button v-if="!showPopup" @click="togglePopup" class="btn btn-primary">
Show Popup
</button>
<div v-if="loading">Loading...</div>
<div v-else-if="error">Error: {{ error }}</div>
<div v-else-if="showPopup && currentPopup"
class="position-fixed bottom-0 end-0 m-4 shadow-lg p-3 bg-white rounded border"
style="z-index: 1080; max-width: 350px;">
<!-- Image section -->
<div v-if="currentPopup.image" class="mb-3">
<img :src="currentPopup.image" alt="Campaign image" class="img-fluid rounded popup-image">
</div>
<h4 class="mb-1 text fw-bold">{{ currentPopup.title }}</h4>
<!-- Added ingress field -->
<p v-if="currentPopup.ingress" class="mb-1 fw-bold small">
{{ currentPopup.ingress }}
</p>
<p class="mb-2 small">
{{ currentPopup.description }}
</p>
<div class="d-flex justify-content-end gap-2">
<button @click="closePopup" class="btn btn-outline-secondary btn-sm">Lukk</button>
<button @click="handleAction" class="btn btn-primary btn-sm">
{{ currentPopup.linkText || 'Se mer' }}
</button>
</div>
</div>
</template>
<style scoped>
.shadow-lg {
box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;
}
.rounded {
border-radius: 0.375rem !important;
}
.popup-image {
width: 100%;
object-fit: cover;
max-height: 180px;
}
</style>

+ 97
- 0
gca-admin-gurusoft-message-dashboard/src/components/SystemLinks.vue View File

@ -0,0 +1,97 @@
<template>
<div v-if="loading">Loading...</div>
<div v-else-if="error">Error: {{ error }}</div>
<div v-else class="container p-3 border overflow-auto" style="max-height: 340px; width: 420px;">
<div
v-for="(link, index) in links"
:key="index"
class="mb-2"
>
<a
:href="link.url"
class="btn d-flex align-items-center text-start w-100 px-4 py-3"
:class="getButtonClass(link.type)"
:style="{ borderRadius: buttonRadius }"
target="_blank"
>
<i :class="getIconClass(link.icon)" class="me-2"></i>
{{ link.text }}
</a>
</div>
</div>
</template>
<script setup>
import {ref, onMounted, defineProps} from 'vue';
import linkData from '@/mocks/systemLinksMockData.json';
const props = defineProps({
elementId: {
type: String,
required: true
},
metrics: {
type: [String, Object, Array],
default: null
},
buttonRadius: {
type: String,
default: '0.0rem'
},
useMockedData: {
type: Boolean,
default: true
}
});
const links = ref([]);
const loading = ref(true);
const error = ref(null);
const getButtonClass = (type) => {
switch (type?.toLowerCase()) {
case 'internal':
return 'btn-secondary opacity-75';
case 'external':
return 'btn-success opacity-75';
case 'admin':
return 'btn-warning opacity-75';
case 'system':
return 'btn-light opacity-75';
default:
return 'btn-info opacity-75';
}
};
const getIconClass = (icon) => `bi bi-${icon}`;
onMounted(async () => {
try {
if (props.useMockedData) {
// Use mock
links.value = linkData;
loading.value = false;
} else {
// Fetch from API
const response = await fetch(`https://TODO-replace-with-API`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
links.value = await response.json();
}
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
});
</script>
<style scoped>
.container {
background-color: #f8f9fa;
border: 1px solid #ddd;
overflow-y: auto;
}
</style>

+ 1
- 1
gca-admin-gurusoft-message-dashboard/src/main.js View File

@ -1,6 +1,6 @@
import './assets/main.css'
import {createApp} from 'vue'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router';

+ 62
- 0
gca-admin-gurusoft-message-dashboard/src/mocks/marketPopUpMockData.json View File

@ -0,0 +1,62 @@
[
{
"title": "Sommertilbud!",
"ingress": "Få opptil 50% rabatt",
"description": "Spar stort på sommerens mest populære varer. Tilbudet varer ut uken.Spar stort på sommerens mest populære varer. Tilbudet varer ut ukenSpar stort på sommerens mest populære varer. Tilbudet varer ut ukenSpar stort på sommerens mest populære varer. Tilbudet varer ut ukenSpar stort på sommerens mest populære varer. Tilbudet varer ut ukenSpar stort på sommerens mest populære varer. Tilbudet varer ut ukenSpar stort på sommerens mest populære varer. Tilbudet varer ut uken",
"url": "https://google.com",
"linkText": "Les mer",
"image": "https://yavuzceliker.github.io/sample-images/image-92.jpg",
"type": "campaign",
"date": "2025-06-27"
},
{
"title": "Ny funksjon i appen",
"ingress": "Chat med oss direkte",
"description": "Vi har lansert en ny chat-funksjon hvor du kan få hjelp på sekunder.",
"url": "https://example.com/chat-funksjon",
"linkText": "Se hvordan",
"image": "https://yavuzceliker.github.io/sample-images/image-632.jpg",
"type": "feature",
"date": "2025-06-26"
},
{
"title": "Viktig informasjon",
"ingress": "Endringer i bruksvilkår",
"description": "Vi har oppdatert våre vilkår og personvernregler. Les mer om endringene her.",
"url": "https://example.com/vilkar",
"linkText": "Les nye vilkår",
"image": "https://yavuzceliker.github.io/sample-images/image-1.jpg",
"type": "notice",
"date": "2025-06-25"
},
{
"title": "Bli med på undersøkelse",
"ingress": "Hjelp oss å bli bedre",
"description": "Ta vår 2-minutters undersøkelse og vær med i trekningen av gavekort.",
"url": "https://example.com/undersokelse",
"linkText": "Svar nå",
"image": "https://yavuzceliker.github.io/sample-images/image-312.jpg",
"type": "survey",
"date": "2025-06-24"
},
{
"title": "Vi er her for deg",
"ingress": "Ny kundeserviceportal",
"description": "Oppdag vårt nye hjelpesenter med artikler, guider og live-hjelp.",
"url": "https://example.com/hjelp",
"linkText": "Gå til hjelpesenter",
"image": "https://yavuzceliker.github.io/sample-images/image-460.jpg",
"type": "support",
"date": "2025-06-23"
},
{
"title": "Eksklusivt webinartilbud",
"ingress": "Lær av ekspertene",
"description": "Bli med på vårt gratis webinar om digital markedsføring. Begrenset med plasser.",
"url": "https://example.com/webinar",
"linkText": "Meld deg på",
"image": "https://yavuzceliker.github.io/sample-images/image-699.jpg",
"type": "event",
"date": "2025-06-22"
}
]

gca-admin-gurusoft-message-dashboard/src/mocks/overlay_popup.json → gca-admin-gurusoft-message-dashboard/src/mocks/overlayPopup.json View File


+ 62
- 0
gca-admin-gurusoft-message-dashboard/src/mocks/systemLinksMockData.json View File

@ -0,0 +1,62 @@
[
{
"url": "https://report.gurusoft.no/",
"text": "Gurusoft hjemmeside",
"type": "admin",
"icon": "dashboard"
},
{
"url": "https://www.gurusoft.no/ta-kontakt?utm_term=gurusoft&utm_campaign=Search+-+Gurusoft+-+Brand&utm_source=adwords&utm_medium=ppc&hsa_acc=1671275617&hsa_cam=21429497412&hsa_grp=164723437872&hsa_ad=704644852789&hsa_src=g&hsa_tgt=kwd-357983112442&hsa_kw=gurusoft&hsa_mt=b&hsa_net=adwords&hsa_ver=3&gad_source=1&gad_campaignid=21429497412&gbraid=0AAAAAC6-h5QA-Uem3fnf9a8Q7l0FhCFkF&gclid=CjwKCAjw3_PCBhA2EiwAkH_j4oYc5_RLO87_7JXoxRBwBR3sR7mHymCM5WRcyM_ORAb7kcmDvoi-HBoCxR4QAvD_BwE",
"text": "Kontakt oss",
"type": "internal",
"icon": "book"
},
{
"url": "https://open.spotify.com/show/4iPq4aLAWHbsXpg0dKSGnY?si=c9ae69117a5740fd&nd=1&dlsi=328d8504668c4558",
"text": "Musikk innslag",
"type": "internal",
"icon": "support"
},
{
"url": "https://www.company.com/privacy-policy",
"text": "Privacy Policy",
"type": "external",
"icon": "policy"
},
{
"url": "https://www.company.com/terms-of-service",
"text": "Terms of Service",
"type": "what",
"icon": "terms"
},
{
"url": "https://intranet.company.local/dashboard",
"text": "Admin Dashboard",
"type": "admin",
"icon": "dashboard"
},
{
"url": "https://google.com",
"text": "Knowledge Base",
"type": "internal",
"icon": "book"
},
{
"url": "https://intranet.company.local/support",
"text": "Support",
"type": "internal",
"icon": "support"
},
{
"url": "https://www.company.com/privacy-policy",
"text": "Privacy Policy",
"type": "external",
"icon": "policy"
},
{
"url": "https://www.company.com/terms-of-service",
"text": "Terms of Service",
"type": "what",
"icon": "terms"
}
]

gca-admin-gurusoft-message-dashboard/src/mocks/system_message.json → gca-admin-gurusoft-message-dashboard/src/mocks/systemMessage.json View File


+ 3
- 1
gca-admin-gurusoft-message-dashboard/src/router/index.js View File

@ -2,11 +2,13 @@ import {createRouter, createWebHistory} from 'vue-router';
import SystemMessagesPage from '../components/SystemMessagesPage.vue';
import SystemMessages from '../components/SystemMesssages.vue';
import Dashboard from '../components/Dashboard.vue';
import SystemLinks from '../components/SystemLinks.vue'
const routes = [
{path: '/systemmessage', name: 'SystemMessage', component: SystemMessages},
{path: '/systemmessagelist', name: 'SystemMessageList', component: SystemMessagesPage},
{path: '/dashboard', name: 'Dashboard', component: Dashboard}
{path: '/systemlinks', name: 'SystemLinks', component: SystemLinks},
{path: '/', name: 'Dashboard', component: Dashboard}
];
const router = createRouter({

+ 2
- 2
gca-admin-gurusoft-message-dashboard/src/utils/api.js View File

@ -1,5 +1,5 @@
import mockSystemMessages from '../mocks/system_message.json';
import mockOverlay from '../mocks/overlay_popup.json';
import mockSystemMessages from '../mocks/systemMessage.json';
import mockOverlay from '../mocks/overlayPopup.json';
const createMockApi = () => {
return {

+ 6
- 0
package-lock.json View File

@ -0,0 +1,6 @@
{
"name": "sommer2025",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

Loading…
Cancel
Save