Merge branch 'devWithMobile' into dev

This commit is contained in:
2025-03-30 16:52:06 +02:00
73 changed files with 4928 additions and 4626 deletions

29
app.vue
View File

@@ -19,7 +19,7 @@ const dev = process.dev
useHead({
title:"FEDEO",
meta: [
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
{ name: 'viewport', content: 'width=device-width, initial-scale=1, viewport-fit=cover,maximum-scale=1.0, user-scalable=no' },
],
link: [
{ rel: 'icon', href: '/favicon.ico' }
@@ -45,14 +45,17 @@ useSeoMeta({
</script>
<template>
<NuxtLayout>
<NuxtPage/>
</NuxtLayout>
<div class="safearea">
<NuxtLayout>
<NuxtPage/>
</NuxtLayout>
<UNotifications/>
<USlideovers />
<UModals />
<VitePwaManifest/>
</div>
<UNotifications/>
<USlideovers />
<UModals />
<VitePwaManifest/>
</template>
@@ -122,5 +125,15 @@ useSeoMeta({
color: #69c350
}
.safearea {
margin-top: env(safe-area-inset-top, 10px) !important;
margin-left: env(safe-area-inset-left, 5px) !important;
margin-right: env(safe-area-inset-right, 5px) !important;
margin-bottom: env(safe-area-inset-bottom, 37px) !important;
/*background-color: grey;*/
}
.scroll {
overflow-y: scroll;
}
</style>

BIN
asssets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
asssets/icons/icon-128.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
asssets/icons/icon-192.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
asssets/icons/icon-256.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
asssets/icons/icon-48.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
asssets/icons/icon-512.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
asssets/icons/icon-72.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
asssets/icons/icon-96.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
asssets/logo-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
asssets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
asssets/splash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

16
capacitor.config.ts Normal file
View File

@@ -0,0 +1,16 @@
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'software.federspiel.fedeo',
appName: 'FEDEO',
webDir: 'dist',
ios: {
handleApplicationNotifications: false
},
/*server: {
url: "http://192.168.1.226:3000",
cleartext: true
}*/
};
export default config;

View File

@@ -7,6 +7,10 @@ const props = defineProps({
required: true,
type: String
},
mode: {
required: true,
type: String
},
createQuery: {
type: Object
},
@@ -82,8 +86,12 @@ const setupCreate = () => {
setupCreate()
const setupQuery = () => {
console.log("setupQuery")
console.log(props.mode)
if(props.mode === "create" && (route.query || props.createQuery)) {
console.log(route.query)
let data = !props.inModal ? route.query : props.createQuery
Object.keys(data).forEach(key => {

View File

@@ -1,5 +1,7 @@
<script setup>
import {useTempStore} from "~/stores/temp.js";
import FloatingActionButton from "~/components/mobile/FloatingActionButton.vue";
import EntityTable from "~/components/EntityTable.vue";
const props = defineProps({
type: {
@@ -9,6 +11,9 @@ const props = defineProps({
items: {
required: true,
type: Array
},
platform: {
required: true,
}
})
@@ -53,11 +58,6 @@ const tempStore = useTempStore()
const dataType = dataStore.dataTypes[type]
//Old
const selectedItem = ref(0)
const selectedColumns = ref(tempStore.columns[type] ? tempStore.columns[type] : dataType.templateColumns.filter(i => !i.disabledInTable))
const columns = computed(() => dataType.templateColumns.filter((column) => !column.disabledInTable && selectedColumns.value.find(i => i.key === column.key)))
@@ -104,7 +104,16 @@ const filteredRows = computed(() => {
</script>
<template>
<FloatingActionButton
:label="`+ ${dataType.labelSingle}`"
variant="outline"
v-if="platform === 'mobile'"
@click="router.push(`/mobile/standardEntity/${type}/create`)"
/>
<UDashboardNavbar :title="dataType.label" :badge="filteredRows.length">
<template #toggle>
<div v-if="platform === 'mobile'"></div>
</template>
<template #right>
<UInput
id="searchinput"
@@ -129,7 +138,7 @@ const filteredRows = computed(() => {
/>
<UButton
v-if="useRole().checkRight(`${type}-create`)"
v-if="platform !== 'mobile' && useRole().checkRight(`${type}-create`)"
@click="router.push(`/standardEntity/${type}/create`)"
class="ml-3"
>+ {{dataType.labelSingle}}</UButton>
@@ -172,53 +181,11 @@ const filteredRows = computed(() => {
</USelectMenu>
</template>
</UDashboardToolbar>
<UTable
:rows="filteredRows"
:columns="columns"
class="w-full"
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
@select="(i) => router.push(`/standardEntity/${type}/show/${i.id}`) "
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: `Keine ${dataType.label} anzuzeigen` }"
>
<template
v-for="column in dataType.templateColumns.filter(i => !i.disabledInTable)"
v-slot:[`${column.key}-header`]="{row}">
<span class="text-nowrap">{{column.label}}</span>
</template>
<template #name-data="{row}">
<span
v-if="row.id === filteredRows[selectedItem].id"
class="text-primary-500 font-bold">{{row.name}}
</span>
<span v-else>
{{row.name}}
</span>
</template>
<template #fullName-data="{row}">
<span
v-if="row.id === filteredRows[selectedItem].id"
class="text-primary-500 font-bold">{{row.fullName}}
</span>
<span v-else>
{{row.fullName}}
</span>
</template>
<template #licensePlate-data="{row}">
<span
v-if="row.id === filteredRows[selectedItem].id"
class="text-primary-500 font-bold">{{row.licensePlate}}
</span>
<span v-else>
{{row.licensePlate}}
</span>
</template>
<template
v-for="column in dataType.templateColumns.filter(i => i.key !== 'name' && i.key !== 'fullName' && i.key !== 'licensePlate' && !i.disabledInTable)"
v-slot:[`${column.key}-data`]="{row}">
<component v-if="column.component" :is="column.component" :row="row"></component>
<span v-else>{{row[column.key] ? `${row[column.key]} ${column.unit ? column.unit : ''}`: ''}}</span>
</template>
</UTable>
<EntityTable
:type="props.type"
:columns="columns"
:rows="filteredRows"
/>
</template>
<style scoped>

View File

@@ -1,5 +1,4 @@
<script setup>
import dayjs from "dayjs";
const props = defineProps({
type: {
@@ -12,6 +11,9 @@ const props = defineProps({
},
inModal: {
type: Boolean,
},
platform: {
type: String,
}
})
@@ -37,95 +39,13 @@ const emit = defineEmits(["updateNeeded"])
const router = useRouter()
const dataStore = useDataStore()
const profileStore = useProfileStore()
const supabase = useSupabaseClient()
const files = useFiles()
const modal = useModal()
const dataType = dataStore.dataTypes[type]
const availableFiles = ref([])
const setup = async () => {
if(props.item.files) {
availableFiles.value = await files.selectSomeDocuments(props.item.files.map(i => i.id)) || []
}
}
setup()
const openTab = ref(0)
const renderedPhases = computed(() => {
console.log(props.item.phases)
if(type === "projects" && props.item.phases) {
return props.item.phases.map((phase,index,array) => {
let isAvailable = false
if(phase.active) {
isAvailable = true
} else if(index > 0 && array[index-1].active ){
isAvailable = true
} else if(index > 1 && array[index-1].optional && array[index-2].active){
isAvailable = true
} else if(array.findIndex(i => i.active) > index) {
isAvailable = true
} else if(phase.label === "Abgeschlossen") {
isAvailable = true
}
return {
...phase,
label: phase.optional ? `${phase.label}(optional)`: phase.label,
disabled: !isAvailable,
defaultOpen: phase.active ? true : false
}
})
} else {
return []
}
})
const changeActivePhase = async (key) => {
let item = await useSupabaseSelectSingle("projects",props.item.id,'*')
let phaseLabel = ""
item.phases = item.phases.map(p => {
if(p.active) p.active = false
if(p.key === key) {
p.active = true
p.activated_at = dayjs().format()
p.activated_by = profileStore.activeProfile.id
phaseLabel = p.label
}
return p
})
const {error:updateError} = await supabase.from("projects").update({phases: item.phases}).eq("id",item.id)
console.log(updateError)
const {error} = await supabase.from("historyitems").insert({
createdBy: profileStore.activeProfile.id,
tenant: profileStore.currentTenant,
text: `Aktive Phase zu "${phaseLabel}" gewechselt`,
project: item.id
})
emit("updateNeeded")
}
const invoiceDeliveryNotes = () => {
router.push(`/createDocument/edit?type=invoices&linkedDocuments=[${props.item.createddocuments.filter(i => i.type === "deliveryNotes").map(i => i.id)}]`)
}
const getAvailableQueryStringData = (keys) => {
let returnString =""
@@ -172,7 +92,27 @@ const getAvailableQueryStringData = (keys) => {
<template>
<UDashboardNavbar
v-if="!props.inModal"
v-if="props.inModal"
:ui="{center: 'flex items-stretch gap-1.5 min-w-0'}"
>
<template #center>
<h1
v-if="item"
:class="['text-xl','font-medium']"
>{{item ? `${dataType.labelSingle}${props.item[dataType.templateColumns.find(i => i.title).key] ? ': ' + props.item[dataType.templateColumns.find(i => i.title).key] : ''}`: '' }}</h1>
</template>
<template #right>
<UButton
@click="modal.close()"
color="red"
class="ml-2"
icon="i-heroicons-x-mark"
variant="outline"
/>
</template>
</UDashboardNavbar>
<UDashboardNavbar
v-else-if="!props.inModal && platform !== 'mobile'"
:ui="{center: 'flex items-stretch gap-1.5 min-w-0'}"
>
<template #left>
@@ -206,388 +146,171 @@ const getAvailableQueryStringData = (keys) => {
</template>
</UDashboardNavbar>
<UDashboardNavbar
v-else
v-else-if="!props.inModal && platform === 'mobile'"
:ui="{center: 'flex items-stretch gap-1.5 min-w-0'}"
>
<!-- <template #left>
<UButton
icon="i-heroicons-chevron-left"
variant="outline"
@click="router.back()/*router.push(`/standardEntity/${type}`)*/"
>
Zurück
</UButton>
<UButton
icon="i-heroicons-chevron-left"
variant="outline"
@click="router.push(`/standardEntity/${type}`)"
>
Übersicht
</UButton>
</template>-->
<template #toggle>
<div></div>
</template>
<template #center>
<h1
v-if="item"
:class="['text-xl','font-medium']"
:class="['text-xl','font-medium','text-truncate']"
>{{item ? `${dataType.labelSingle}${props.item[dataType.templateColumns.find(i => i.title).key] ? ': ' + props.item[dataType.templateColumns.find(i => i.title).key] : ''}`: '' }}</h1>
</template>
<template #right>
<UButton
@click="modal.close()"
color="red"
class="ml-2"
icon="i-heroicons-x-mark"
variant="outline"
/>
@click="router.push(`/standardEntity/${type}/edit/${item.id}`)"
>
Bearbeiten
</UButton>
</template>
</UDashboardNavbar>
<UTabs
:items="dataType.showTabs"
v-if="props.item.id"
v-if="props.item.id && platform !== 'mobile'"
class="p-5"
v-model="openTab"
>
<template #item="{item:tab}">
<div class="scroll">
<div v-if="tab.label === 'Informationen'" class="flex flex-row mt-5">
<UCard class="w-1/2 mr-5">
<UAlert
v-if="item.archived"
color="rose"
variant="outline"
:title="`${dataType.labelSingle} archiviert`"
icon="i-heroicons-archive-box"
class="mb-5"
/>
<div class="text-wrap">
<table class="w-full">
<tbody>
<tr
v-for="datapoint in dataType.templateColumns"
>
<td>{{datapoint.label}}:</td>
<td>
<component v-if="datapoint.component" :is="datapoint.component" :row="props.item" :in-show="true"></component>
<div v-else>
<span v-if="datapoint.key.includes('.')">{{props.item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]}}{{datapoint.unit}}</span>
<span v-else>{{props.item[datapoint.key]}} {{datapoint.unit}}</span>
</div>
</td>
</tr>
</tbody>
<div v-if="tab.label === 'Informationen'" class="flex flex-row">
</table>
</div>
<EntityShowSubInformation
:top-level-type="type"
:item="props.item"
class="w-1/2 mr-5"
:platform="platform"
/>
</UCard>
<UCard class="w-1/2">
<HistoryDisplay
:type="type.substring(0,type.length-1)"
v-if="props.item.id"
:element-id="props.item.id"
render-headline
/>
</UCard>
</div>
<div v-else-if="tab.label === 'Dateien'">
<UCard class="mt-5">
<Toolbar>
<DocumentUpload
:type="type.substring(0,type.length-1)"
:element-id="item.id"
@uploadFinished="emit('updateNeeded')"
/>
</Toolbar>
<DocumentList
:key="props.item.files.length"
:documents="availableFiles"
v-if="availableFiles.length > 0"
/>
<UAlert
v-else
icon="i-heroicons-x-mark"
title="Keine Dateien verfügbar"
/>
</UCard>
</div>
<div v-else-if="tab.label === 'Projekte'">
<UCard class="mt-5">
<Toolbar>
<UButton
@click="router.push(`/standardEntity/projects/create?${type.substring(0,type.length-1)}=${props.item.id}`)"
>
+ Projekt
</UButton>
</Toolbar>
<UTable
:rows="props.item.projects"
@select="(row) => router.push(`/standardEntity/projects/show/${row.id}`)"
:columns="[{label: 'Name', key: 'name'},{label: 'Phase', key: 'phase'}]"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine zugehörigen Projekte' }"
>
<template #phase-data="{row}">
{{row.phases ? row.phases.find(i => i.active).label : ""}}
</template>
</UTable>
</UCard>
</div>
<div v-else-if="tab.label === 'Ansprechpartner'">
<UCard class="mt-5">
<Toolbar>
<UButton
@click="router.push(`/standardEntity/contacts/create?${type.substring(0,type.length-1)}=${props.item.id}`)"
>
+ Ansprechpartner
</UButton>
</Toolbar>
<UTable
:rows="props.item.contacts"
@select="(row) => router.push(`/standardEntity/contacts/show/${row.id}`)"
:columns="[{label: 'Name', key: 'fullName'},{label: 'Rolle', key: 'role'}]"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine zugehörigen Ansprechpartner' }"
>
</UTable>
</UCard>
</div>
<div v-else-if="tab.label === 'Objekte'">
<UCard class="mt-5">
<Toolbar>
<UButton
@click="router.push(`/standardEntity/plants/create?${type.substring(0,type.length-1)}=${props.item.id}`)"
>
+ Objekt
</UButton>
<UButton
v-if="type === 'customers'"
@click="router.push(`/standardEntity/plants/create?${type.substring(0,type.length-1)}=${props.item.id}&name=${encodeURIComponent(`${props.item.infoData.street}, ${props.item.infoData.zip} ${props.item.infoData.city}`)}`)"
>
+ Kundenadresse als Objekt
</UButton>
</Toolbar>
<UTable
:rows="props.item.plants"
@select="(row) => router.push(`/standardEntity/plants/show/${row.id}`)"
:columns="[{label: 'Name', key: 'name'}]"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine zugehörigen Objekte' }"
>
</UTable>
</UCard>
</div>
<div v-else-if="tab.label === 'Aufgaben'">
<UCard class="mt-5">
<Toolbar>
<UButton
@click="router.push(`/standardEntity/tasks/create?${type.substring(0,type.length-1)}=${props.item.id}`)"
>
+ Aufgabe
</UButton>
</Toolbar>
<UTable
:rows="props.item.tasks"
@select="(row) => router.push(`/standardEntity/tasks/show/${row.id}`)"
:columns="[{label: 'Name', key: 'name'}]"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine zugehörigen Aufgaben' }"
/>
</UCard>
</div>
<div v-else-if="tab.label === 'Verträge'">
<UCard class="mt-5">
<Toolbar>
<UButton
@click="router.push(`/standardEntity/contracts/create?${type.substring(0,type.length-1)}=${props.item.id}`)"
>
+ Vertrag
</UButton>
</Toolbar>
<UTable
:rows="props.item.contracts"
@select="(row) => router.push(`/standardEntity/contracts/show/${row.id}`)"
:columns="[{label: 'Name', key: 'name'},{label: 'Aktiv', key: 'active'}]"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine zugehörigen Verträge' }"
></UTable>
</UCard>
</div>
<div v-else-if="tab.label === 'Überprüfungen'">
<UCard class="mt-5">
<UTable
:rows="props.item.checks"
:columns="[{key:'name',label: 'Name'},{key:'rhythm',label: 'Rhythmus'},{key:'description',label: 'Beschreibung'}]"
class="w-full"
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
@select="(i) => router.push(`/checks/show/${i.id}`) "
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Überprüfungen anzuzeigen' }"
>
<template #rhythm-data="{row}">
{{row.distance}}
<span v-if="row.distanceUnit === 'dayjs'">Tage</span>
<span v-if="row.distanceUnit === 'years'">Jahre</span>
</template>
</UTable>
</UCard>
</div>
<div v-else-if="tab.label === 'Phasen'">
<UCard class="mt-5">
<UAccordion
:items="renderedPhases"
>
<template #default="{item,index,open}">
<UButton
variant="ghost"
:color="item.active ? 'primary' : 'white'"
class="mb-1"
:disabled="true"
>
<template #leading>
<div class="w-6 h-6 flex items-center justify-center -my-1">
<UIcon :name="item.icon" class="w-4 h-4 " />
</div>
</template>
<span class="truncate"> {{item.label}}</span>
<template #trailing>
<UIcon
name="i-heroicons-chevron-right-20-solid"
class="w-5 h-5 ms-auto transform transition-transform duration-200"
:class="[open && 'rotate-90']"
/>
</template>
</UButton>
</template>
<template #item="{item, index}">
<UCard class="mx-5">
<template #header>
<span class="dark:text-white text-black">{{item.label}}</span>
</template>
<InputGroup>
<!-- TODO: Reactive Change Phase -->
<UButton
v-if="!item.activated_at && index !== 0 "
@click="changeActivePhase(item.key)"
>
Phase aktivieren
</UButton>
<UButton
v-if="item.active"
v-for="button in item.quickactions"
@click="router.push(`${button.link}&customer=${props.item.customer.id}&project=${props.item.id}`)"
>
{{button.label}}
</UButton>
</InputGroup>
<div>
<p v-if="item.activated_at" class="dark:text-white text-black">Aktiviert am: {{dayjs(item.activated_at).format("DD.MM.YY HH:mm")}} Uhr</p>
<p v-if="item.activated_by" class="dark:text-white text-black">Aktiviert durch: {{profileStore.getProfileById(item.activated_by).fullName}}</p>
<p v-if="item.description" class="dark:text-white text-black">Beschreibung: {{item.description}}</p>
</div>
</UCard>
</template>
</UAccordion>
</UCard>
</div>
<div v-else-if="tab.label === 'Ausgangsbelege'">
<UCard class="mt-5">
<Toolbar>
<UButton
@click="router.push(`/createDocument/edit/?${getAvailableQueryStringData({type: 'quotes'})}`)"
>
+ Angebot
</UButton>
<UButton
@click="router.push(`/createDocument/edit/?${getAvailableQueryStringData({type: 'confirmationOrders'})}`)"
>
+ Auftragsbestätigung
</UButton>
<UButton
@click="router.push(`/createDocument/edit/?${getAvailableQueryStringData({type: 'deliveryNotes'})}`)"
>
+ Lieferschein
</UButton>
<UButton
@click="router.push(`/createDocument/edit/?${getAvailableQueryStringData({type: 'advanceInvoices'})}`)"
>
+ Abschlagsrechnung
</UButton>
<UButton
@click="router.push(`/createDocument/edit/?${getAvailableQueryStringData({type: 'invoices'})}`)"
>
+ Rechnung
</UButton>
<UButton
@click="invoiceDeliveryNotes"
v-if="type === 'projects'"
>
Lieferscheine abrechnen
</UButton>
</Toolbar>
<UTable
:rows="props.item.createddocuments"
:columns="[{key:'state',label: 'Status'},{key:'type',label: 'Typ'},{key:'documentNumber',label: 'Nummer'},{key:'documentDate',label: 'Datum'},{key:'description',label: 'Beschreibung'}]"
@select="(i) => router.push(i.state === 'Entwurf' ? `/createDocument/edit/${i.id}`:`/createDocument/show/${i.id}`)"
>
<template #documentDate-data="{row}">
{{dayjs(row.documentDate).format("DD.MM.YY HH:mm")}}
</template>
<template #type-data="{row}">
{{dataStore.documentTypesForCreation[row.type].labelSingle}}
</template>
</UTable>
</UCard>
</div>
<div v-else-if="tab.label === 'Termine'">
<UCard class="mt-5">
<Toolbar>
<UButton
@click="router.push(`/standardEntity/events/create/?${getAvailableQueryStringData()}`)"
>
+ Termin
</UButton>
</Toolbar>
<UTable
:rows="props.item.events"
@select="(i) => router.push(`/standardEntity/events/show/${i.id}`)"
:columns="[{key:'name',label: 'Name'},{key:'eventtype',label: 'Typ'},{key:'startDate',label: 'Start'},{key:'endDate',label: 'Ende'},{key:'notes',label: 'Notizen'}]"
>
<template #startDate-data="{row}">
{{dayjs(row.documentDate).format("DD.MM.YY HH:mm")}}
</template>
<template #endDate-data="{row}">
{{dayjs(row.documentDate).format("DD.MM.YY HH:mm")}}
</template>
</UTable>
</UCard>
</div>
<div v-else-if="tab.label === 'Auswertung Kostenstelle'">
<UCard class="mt-5">
<costcentre-display :item="props.item"/>
</UCard>
</div>
<EntityShowSubHistoryDisplay
:top-level-type="type"
:item="props.item"
class="w-1/2"
:platform="platform"
/>
</div>
<EntityShowSubFiles
:item="props.item"
:query-string-data="getAvailableQueryStringData()"
v-else-if="tab.label === 'Dateien'"
:top-level-type="type"
type="files"
@updateNeeded="emit('updateNeeded')"
:platform="platform"
/>
<EntityShowSubPhases
:item="props.item"
:top-level-type="type"
v-else-if="tab.label === 'Phasen'"
:query-string-data="getAvailableQueryStringData()"
@updateNeeded="emit('updateNeeded')"
:platform="platform"
/>
<EntityShowSubCreatedDocuments
:item="props.item"
:top-level-type="type"
v-else-if="tab.label === 'Ausgangsbelege'"
:query-string-data="getAvailableQueryStringData()"
:platform="platform"
/>
<EntityShowSubCostCentreReport
:top-level-type="type"
:item="props.item"
v-else-if="tab.label === 'Auswertung Kostenstelle'"
:platform="platform"
/>
<EntityShowSub
:item="props.item"
:query-string-data="getAvailableQueryStringData()"
:tab-label="tab.label"
:top-level-type="type"
v-else
:platform="platform"
/>
</template>
</UTabs>
<UDashboardPanelContent v-else style="overflow-x: hidden;">
<div v-for="sub in dataType.showTabs" :key="sub.key">
<div v-if="sub.label === 'Informationen'">
<EntityShowSubInformation
:top-level-type="type"
:item="props.item"
:platform="platform"
/>
<EntityShowSubHistoryDisplay
:top-level-type="type"
:item="props.item"
:platform="platform"
/>
</div>
<EntityShowSubFiles
:item="props.item"
:query-string-data="getAvailableQueryStringData()"
v-else-if="sub.label === 'Dateien'"
:top-level-type="type"
type="files"
@updateNeeded="emit('updateNeeded')"
:platform="platform"
/>
<EntityShowSubPhases
:item="props.item"
:top-level-type="type"
v-else-if="sub.label === 'Phasen'"
:query-string-data="getAvailableQueryStringData()"
@updateNeeded="emit('updateNeeded')"
:platform="platform"
/>
<EntityShowSubCreatedDocuments
:item="props.item"
:top-level-type="type"
v-else-if="sub.label === 'Ausgangsbelege'"
:query-string-data="getAvailableQueryStringData()"
:platform="platform"
/>
<EntityShowSubCostCentreReport
:top-level-type="type"
:item="props.item"
v-else-if="sub.label === 'Auswertung Kostenstelle'"
:platform="platform"
/>
<EntityShowSub
:item="props.item"
:query-string-data="getAvailableQueryStringData()"
:tab-label="sub.label"
:top-level-type="type"
v-else
:platform="platform"
/>
</div>
</UDashboardPanelContent>
</template>
<style scoped>
td {
border-bottom: 1px solid lightgrey;
vertical-align: top;
padding-bottom: 0.15em;
padding-top: 0.15em;
}
.scroll {
height: 80vh;
overflow-y: scroll;
padding: 0.3em;
}
</style>

View File

@@ -0,0 +1,125 @@
<script setup>
const props = defineProps({
queryStringData: {
type: String
},
item: {
type: Object,
required: true
},
tabLabel: {
type: String,
required: true
},
type: {
type: String,
},
topLevelType: {
type: String,
required: true
},
platform: {
type: String,
required: true
}
})
let type = ref("")
const dataStore = useDataStore()
const tempStore = useTempStore()
const router = useRouter()
let dataType = null
const selectedColumns = ref(null)
const columns = computed(() => dataType.templateColumns.filter((column) => !column.disabledInTable && selectedColumns.value.find(i => i.key === column.key)))
const loaded = ref(false)
const setup = () => {
if(!props.type && props.tabLabel ) {
if(props.tabLabel === "Aufgaben") {
type.value = "tasks"
} else if(props.tabLabel === "Projekte") {
type.value = "projects"
} else if(props.tabLabel === "Termine") {
type.value = "events"
} else if(props.tabLabel === "Objekte") {
type.value = "plants"
} else if(props.tabLabel === "Ansprechpartner") {
type.value = "contacts"
} else if(props.tabLabel === "Verträge") {
type.value = "contracts"
} else if(props.tabLabel === "Überprüfungen") {
type.value = "checks"
}
} else {
type.value = props.type
}
dataType = dataStore.dataTypes[type.value]
selectedColumns.value = tempStore.columns[type.value] ? tempStore.columns[type.value] : dataType.templateColumns.filter(i => !i.disabledInTable)
loaded.value = true
}
setup()
</script>
<template>
<UCard class="mt-5" v-if="loaded" :style="props.platform !== 'mobile' ? 'height: 80vh' : ''">
<template #header v-if="props.platform === 'mobile'">
<span>{{dataType.label}}</span>
</template>
<Toolbar>
<UButton
@click="router.push(`/standardEntity/${type}/create?${props.queryStringData}`)"
>
+ {{dataType.labelSingle}}
</UButton>
<UButton
v-if="props.topLevelType === 'customers' && type === 'plants'"
@click="router.push(`/standardEntity/plants/create?${props.queryStringData}&name=${encodeURIComponent(`${props.item.infoData.street}, ${props.item.infoData.zip} ${props.item.infoData.city}`)}`)"
>
+ Kundenadresse als Objekt
</UButton>
<template #right>
<USelectMenu
v-model="selectedColumns"
icon="i-heroicons-adjustments-horizontal-solid"
:options="dataType.templateColumns.filter(i => !i.disabledInTable)"
multiple
class="hidden lg:block"
by="key"
:color="selectedColumns.length !== dataType.templateColumns.filter(i => !i.disabledInTable).length ? 'primary' : 'white'"
:ui-menu="{ width: 'min-w-max' }"
@change="tempStore.modifyColumns(type,selectedColumns)"
>
<template #label>
Spalten
</template>
</USelectMenu>
</template>
</Toolbar>
<EntityTable
:type="type"
:columns="columns"
:rows="props.item[type]"
/>
</UCard>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,35 @@
<script setup>
const props = defineProps({
queryStringData: {
type: String
},
item: {
type: Object,
required: true
},
topLevelType: {
type: String,
required: true
},
platform: {
type: String,
required: true
}
})
</script>
<template>
<UCard class="mt-5 scroll" :style="props.platform !== 'mobile' ? 'height: 80vh' : ''">
<template #header v-if="props.platform === 'mobile'">
<span>Auswertung</span>
</template>
<costcentre-display :item="props.item"/>
</UCard>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,235 @@
<script setup>
import dayjs from "dayjs";
import {useSum} from "~/composables/useSum.js";
import {useSupabaseSelect} from "~/composables/useSupabase.js";
defineShortcuts({
/*'/': () => {
//console.log(searchinput)
//searchinput.value.focus()
document.getElementById("searchinput").focus()
},*/
'Enter': {
usingInput: true,
handler: () => {
router.push(`/standardEntity/${props.topLevelType}/show/${props.item.createddocuments.value[selectedItem.value].id}`)
}
},
'arrowdown': () => {
if(selectedItem.value < props.item.createddocuments.length - 1) {
selectedItem.value += 1
} else {
selectedItem.value = 0
}
},
'arrowup': () => {
if(selectedItem.value === 0) {
selectedItem.value = props.item.createddocuments.length - 1
} else {
selectedItem.value -= 1
}
}
})
const props = defineProps({
queryStringData: {
type: String
},
item: {
type: Object,
required: true
},
type: {
type: String,
required: true
},
topLevelType: {
type: String,
required: true
},
platform: {
type: String,
required: true
}
})
const dataStore = useDataStore()
const tempStore = useTempStore()
const router = useRouter()
const createddocuments = ref([])
const setup = async () => {
createddocuments.value = await useSupabaseSelect("createddocuments")
}
setup()
const templateColumns = [
{
key: "reference",
label: "Referenz",
sortable: true
},
{
key: 'type',
label: "Typ",
sortable: true
},{
key: 'state',
label: "Status",
sortable: true
},
{
key: "date",
label: "Datum",
sortable: true
},
{
key: "dueDate",
label: "Fällig",
sortable: true
}
]
const selectedColumns = ref(tempStore.columns["createddocuments"] ? tempStore.columns["createddocuments"] : templateColumns)
const columns = computed(() => templateColumns.filter((column) => selectedColumns.value.find(i => i.key === column.key)))
const selectedItem = ref(0)
const getAvailableQueryStringData = (keys) => {
let returnString = props.queryStringData
function addParam (key,value) {
if(returnString.length === 0) {
returnString += `${key}=${value}`
} else {
returnString += `&${key}=${value}`
}
}
if(keys) {
Object.keys(keys).forEach(key => {
addParam(key, keys[key])
})
}
return returnString
}
const invoiceDeliveryNotes = () => {
router.push(`/createDocument/edit?type=invoices&linkedDocuments=[${props.item.createddocuments.filter(i => i.type === "deliveryNotes").map(i => i.id)}]`)
}
</script>
<template>
<UCard class="mt-5" :style="props.platform !== 'mobile' ? 'height: 80vh' : ''">
<template #header v-if="props.platform === 'mobile'">
<span>Ausgangsbelege</span>
</template>
<Toolbar>
<UButton
@click="invoiceDeliveryNotes"
v-if="props.topLevelType === 'projects'"
>
Lieferscheine abrechnen
</UButton>
<UButton
@click="router.push(`/createDocument/edit/?${getAvailableQueryStringData({type: 'quotes'})}`)"
>
+ Angebot
</UButton>
<UButton
@click="router.push(`/createDocument/edit/?${getAvailableQueryStringData({type: 'confirmationOrders'})}`)"
>
+ Auftragsbestätigung
</UButton>
<UButton
@click="router.push(`/createDocument/edit/?${getAvailableQueryStringData({type: 'deliveryNotes'})}`)"
>
+ Lieferschein
</UButton>
<UButton
@click="router.push(`/createDocument/edit/?${getAvailableQueryStringData({type: 'advanceInvoices'})}`)"
>
+ Abschlagsrechnung
</UButton>
<UButton
@click="router.push(`/createDocument/edit/?${getAvailableQueryStringData({type: 'invoices'})}`)"
>
+ Rechnung
</UButton>
<template #right>
<USelectMenu
v-model="selectedColumns"
icon="i-heroicons-adjustments-horizontal-solid"
:options="templateColumns"
multiple
class="hidden lg:block"
by="key"
@change="tempStore.modifyColumns('createddocuments',selectedColumns)"
>
<template #label>
Spalten
</template>
</USelectMenu>
</template>
</Toolbar>
<UTable
:rows="props.item.createddocuments"
:columns="columns"
class="w-full"
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
@select="selectItem"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Belege anzuzeigen' }"
>
<template #type-data="{row}">
{{dataStore.documentTypesForCreation[row.type].labelSingle}}
</template>
<template #state-data="{row}">
<span
v-if="row.state === 'Entwurf'"
class="text-rose-500"
>
{{row.state}}
</span>
<span
v-if="row.state === 'Gebucht'"
class="text-cyan-500"
>
{{row.state}}
</span>
<span
v-if="row.state === 'Abgeschlossen'"
class="text-primary-500"
>
{{row.state}}
</span>
</template>
<template #reference-data="{row}">
<span v-if="row === props.item.createddocuments[selectedItem]" class="text-primary-500 font-bold">{{row.documentNumber}}</span>
<span v-else>{{row.documentNumber}}</span>
</template>
<template #date-data="{row}">
<span v-if="row.date">{{row.date ? dayjs(row.date).format("DD.MM.YY") : ''}}</span>
<span v-if="row.documentDate">{{row.documentDate ? dayjs(row.documentDate).format("DD.MM.YY") : ''}}</span>
</template>
<template #dueDate-data="{row}">
<span v-if="row.paymentDays && ['invoices','advanceInvoices'].includes(row.type)" >{{row.documentDate ? dayjs(row.documentDate).add(row.paymentDays,'day').format("DD.MM.YY") : ''}}</span>
</template>
<!-- <template #amount-data="{row}">
<span v-if="row.type !== 'deliveryNotes'">{{useCurrency(useSum().getCreatedDocumentSum(row, createddocuments))}}</span>
</template>-->
</UTable>
</UCard>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,70 @@
<script setup>
const props = defineProps({
queryStringData: {
type: String
},
item: {
type: Object,
required: true
},
type: {
type: String,
required: true
},
topLevelType: {
type: String,
required: true
},
platform: {
type: String,
required: true
}
})
const emit = defineEmits(["updateNeeded"])
const files = useFiles()
const availableFiles = ref([])
const setup = async () => {
if(props.item.files) {
availableFiles.value = await files.selectSomeDocuments(props.item.files.map(i => i.id)) || []
}
}
setup()
</script>
<template>
<UCard class="mt-5" :style="props.platform !== 'mobile' ? 'height: 80vh' : ''">
<template #header v-if="props.platform === 'mobile'">
<span>Dateien</span>
</template>
<Toolbar>
<DocumentUpload
:type="props.type.substring(0,props.type.length-1)"
:element-id="props.item.id"
@uploadFinished="emit('updateNeeded')"
/>
</Toolbar>
<DocumentList
:key="props.item.files.length"
:documents="availableFiles"
v-if="availableFiles.length > 0"
/>
<UAlert
v-else
icon="i-heroicons-x-mark"
title="Keine Dateien verfügbar"
/>
</UCard>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,42 @@
<script setup>
const props = defineProps({
queryStringData: {
type: String
},
item: {
type: Object,
required: true
},
topLevelType: {
type: String,
required: true
},
platform: {
type: String,
required: true
}
})
const dataStore = useDataStore()
const dataType = dataStore.dataTypes[props.topLevelType]
</script>
<template>
<UCard class="mt-5 scroll" :style="props.platform !== 'mobile' ? 'height: 80vh' : ''">
<HistoryDisplay
:type="props.topLevelType.substring(0,props.topLevelType.length-1)"
v-if="props.item.id"
:element-id="props.item.id"
render-headline
/>
<!--TODO Workaround für die Höhe finden? Evt unterseite oder Modal oder ganz nach unten -->
</UCard>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,77 @@
<script setup>
const props = defineProps({
queryStringData: {
type: String
},
item: {
type: Object,
required: true
},
topLevelType: {
type: String,
required: true
},
platform: {
type: String,
required: true
}
})
const dataStore = useDataStore()
const tempStore = useTempStore()
const router = useRouter()
const dataType = dataStore.dataTypes[props.topLevelType]
// const selectedColumns = ref(tempStore.columns[props.topLevelType] ? tempStore.columns[props.topLevelType] : dataType.templateColumns.filter(i => !i.disabledInTable))
// const columns = computed(() => dataType.templateColumns.filter((column) => !column.disabledInTable && selectedColumns.value.find(i => i.key === column.key)))
</script>
<template>
<UCard class="mt-5 scroll" :style="props.platform !== 'mobile' ? 'height: 80vh' : ''">
<template #header v-if="props.platform === 'mobile'">
<span>Informationen</span>
</template>
<UAlert
v-if="props.item.archived"
color="rose"
variant="outline"
:title="`${dataType.labelSingle} archiviert`"
icon="i-heroicons-archive-box"
class="mb-5"
/>
<div class="text-wrap">
<table class="w-full">
<tbody>
<tr
v-for="datapoint in dataType.templateColumns"
>
<td>{{datapoint.label}}:</td>
<td>
<component v-if="datapoint.component" :is="datapoint.component" :row="props.item" :in-show="true"></component>
<div v-else>
<span v-if="datapoint.key.includes('.')">{{props.item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]}}{{datapoint.unit}}</span>
<span v-else>{{props.item[datapoint.key]}} {{datapoint.unit}}</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</UCard>
</template>
<style scoped>
td {
border-bottom: 1px solid lightgrey;
vertical-align: top;
padding-bottom: 0.15em;
padding-top: 0.15em;
}
</style>

View File

@@ -0,0 +1,167 @@
<script setup>
import dayjs from "dayjs";
const props = defineProps({
queryStringData: {
type: String
},
item: {
type: Object,
required: true
},
topLevelType: {
type: String,
required: true
},
platform: {
type: String,
required: true
}
})
const emit = defineEmits(["updateNeeded"]);
const router = useRouter()
const profileStore = useProfileStore()
const supabase = useSupabaseClient()
const renderedPhases = computed(() => {
if(props.topLevelType === "projects" && props.item.phases) {
return props.item.phases.map((phase,index,array) => {
let isAvailable = false
if(phase.active) {
isAvailable = true
} else if(index > 0 && array[index-1].active ){
isAvailable = true
} else if(index > 1 && array[index-1].optional && array[index-2].active){
isAvailable = true
} else if(array.findIndex(i => i.active) > index) {
isAvailable = true
} else if(phase.label === "Abgeschlossen") {
isAvailable = true
}
return {
...phase,
label: phase.optional ? `${phase.label}(optional)`: phase.label,
disabled: !isAvailable,
defaultOpen: phase.active ? true : false
}
})
} else {
return []
}
})
const changeActivePhase = async (key) => {
let item = await useSupabaseSelectSingle("projects",props.item.id,'*')
let phaseLabel = ""
item.phases = item.phases.map(p => {
if(p.active) p.active = false
if(p.key === key) {
p.active = true
p.activated_at = dayjs().format()
p.activated_by = profileStore.activeProfile.id
phaseLabel = p.label
}
return p
})
const {error:updateError} = await supabase.from("projects").update({phases: item.phases}).eq("id",item.id)
const {error} = await supabase.from("historyitems").insert({
createdBy: profileStore.activeProfile.id,
tenant: profileStore.currentTenant,
text: `Aktive Phase zu "${phaseLabel}" gewechselt`,
project: item.id
})
emit("updateNeeded")
}
</script>
<template>
<UCard class="mt-5 scroll" :style="props.platform !== 'mobile' ? 'height: 80vh' : ''">
<template #header v-if="props.platform === 'mobile'">
<span>Phasen</span>
</template>
<UAccordion
:items="renderedPhases"
>
<template #default="{item,index,open}">
<UButton
variant="ghost"
:color="item.active ? 'primary' : 'white'"
class="mb-1"
:disabled="true"
>
<template #leading>
<div class="w-6 h-6 flex items-center justify-center -my-1">
<UIcon :name="item.icon" class="w-4 h-4 " />
</div>
</template>
<span class="truncate"> {{item.label}}</span>
<template #trailing>
<UIcon
name="i-heroicons-chevron-right-20-solid"
class="w-5 h-5 ms-auto transform transition-transform duration-200"
:class="[open && 'rotate-90']"
/>
</template>
</UButton>
</template>
<template #item="{item, index}">
<UCard class="mx-5">
<template #header>
<span class="dark:text-white text-black">{{item.label}}</span>
</template>
<InputGroup>
<!-- TODO: Reactive Change Phase -->
<UButton
v-if="!item.activated_at && index !== 0 "
@click="changeActivePhase(item.key)"
>
Phase aktivieren
</UButton>
<UButton
v-if="item.active"
v-for="button in item.quickactions"
@click="router.push(`${button.link}&${props.queryStringData}`)"
>
{{button.label}}
</UButton>
</InputGroup>
<div>
<p v-if="item.activated_at" class="dark:text-white text-black">Aktiviert am: {{dayjs(item.activated_at).format("DD.MM.YY HH:mm")}} Uhr</p>
<p v-if="item.activated_by" class="dark:text-white text-black">Aktiviert durch: {{profileStore.getProfileById(item.activated_by).fullName}}</p>
<p v-if="item.description" class="dark:text-white text-black">Beschreibung: {{item.description}}</p>
</div>
</UCard>
</template>
</UAccordion>
</UCard>
</template>
<style scoped>
</style>

110
components/EntityTable.vue Normal file
View File

@@ -0,0 +1,110 @@
<script setup>
defineShortcuts({
/*'/': () => {
//console.log(searchinput)
//searchinput.value.focus()
document.getElementById("searchinput").focus()
},*/
'Enter': {
usingInput: true,
handler: () => {
router.push(`/standardEntity/${props.type}/show/${props.rows.value[selectedItem.value].id}`)
}
},
'arrowdown': () => {
if(selectedItem.value < props.rows.length - 1) {
selectedItem.value += 1
} else {
selectedItem.value = 0
}
},
'arrowup': () => {
if(selectedItem.value === 0) {
selectedItem.value = props.rows.length - 1
} else {
selectedItem.value -= 1
}
}
})
const props = defineProps({
rows: {
type: Array,
required: true,
default: []
},
columns: {
type: Array,
required: true,
},
type: {
type: String,
required: true,
}
})
const dataStore = useDataStore()
const router = useRouter()
const dataType = dataStore.dataTypes[props.type]
const selectedItem = ref(0)
</script>
<template>
<UTable
v-if="dataType && columns"
:rows="props.rows"
:columns="props.columns"
class="w-full"
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
@select="(i) => router.push(`/standardEntity/${type}/show/${i.id}`) "
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: `Keine ${dataType.label} anzuzeigen` }"
>
<template
v-for="column in dataType.templateColumns.filter(i => !i.disabledInTable)"
v-slot:[`${column.key}-header`]="{row}">
<span class="text-nowrap">{{column.label}}</span>
</template>
<template #name-data="{row}">
<span
v-if="row.id === props.rows[selectedItem].id"
class="text-primary-500 font-bold">{{row.name}}
</span>
<span v-else>
{{row.name}}
</span>
</template>
<template #fullName-data="{row}">
<span
v-if="row.id === props.rows[selectedItem].id"
class="text-primary-500 font-bold">{{row.fullName}}
</span>
<span v-else>
{{row.fullName}}
</span>
</template>
<template #licensePlate-data="{row}">
<span
v-if="row.id === props.rows[selectedItem].id"
class="text-primary-500 font-bold">{{row.licensePlate}}
</span>
<span v-else>
{{row.licensePlate}}
</span>
</template>
<template
v-for="column in dataType.templateColumns.filter(i => i.key !== 'name' && i.key !== 'fullName' && i.key !== 'licensePlate' && !i.disabledInTable)"
v-slot:[`${column.key}-data`]="{row}">
<component v-if="column.component" :is="column.component" :row="row"></component>
<span v-else>{{row[column.key] ? `${row[column.key]} ${column.unit ? column.unit : ''}`: ''}}</span>
</template>
</UTable>
</template>
<style scoped>
</style>

View File

@@ -3,9 +3,14 @@
</script>
<template>
<InputGroup>
<slot/>
</InputGroup>
<div class="flex flex-row justify-between">
<InputGroup>
<slot />
</InputGroup>
<InputGroup>
<slot name="right"/>
</InputGroup>
</div>
<UDivider class="my-3"/>
</template>

View File

@@ -0,0 +1,26 @@
<script setup>
const supabase = useSupabaseClient()
const router = useRouter()
const profileStore = useProfileStore()
const tenant = ref({})
const setupPage = async () => {
tenant.value = (await supabase.from("tenants").select().eq("id",profileStore.currentTenant).single()).data
}
setupPage()
</script>
<template>
<div>
<h1 class="font-bold text-xl">Willkommen zurück {{profileStore.activeProfile.fullName}}</h1>
<span v-if="tenant.id">bei {{tenant.name}}</span>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,47 @@
<script setup>
const props = defineProps({
label: {
type: String,
required: true,
},
icon: {
type: String,
},
variant: {
type: String,
default: 'solid'
},
color: {
type: String,
default: 'primary'
},
pos: {
type: Number,
default: 0
}
})
const emit = defineEmits(['click'])
</script>
<template>
<UButton
id="fab"
:icon="props.icon"
:label="props.label"
:variant="props.variant"
:color="props.color"
@click="emit('click')"
:style="`bottom: ${15 + props.pos * 5}vh;`"
class="bg-white dark:bg-gray-950"
/>
</template>
<style scoped>
#fab {
position: fixed;
right: 15px;
z-index: 5;
}
</style>

View File

@@ -0,0 +1,26 @@
import {Capacitor} from "@capacitor/core";
import {Device} from "@capacitor/device";
import {Network} from "@capacitor/network";
export const useCapacitor = () => {
const getPlatform = () => {
return Capacitor.getPlatform()
}
const getDeviceInfo = async () => {
return await Device.getInfo()
}
const getIsPhone = async () => {
let deviceInfo = await useCapacitor().getDeviceInfo()
return deviceInfo.model.toLowerCase().includes('iphone')
}
const getNetworkStatus = async () => {
return await Network.getStatus()
}
return {getPlatform, getDeviceInfo, getNetworkStatus, getIsPhone}
}

View File

@@ -1,4 +1,3 @@
import index from "v-calendar";
export const useFiles = () => {
const supabase = useSupabaseClient()

13
ios/.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
App/build
App/Pods
App/output
App/App/public
DerivedData
xcuserdata
# Cordova plugins for Capacitor
capacitor-cordova-ios-plugins
# Generated Config files
App/App/capacitor.config.json
App/App/config.xml

View File

@@ -0,0 +1,631 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 48;
objects = {
/* Begin PBXBuildFile section */
2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; };
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; };
504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; };
504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; };
504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
7E144E961F6CA2C63512098E /* Pods_OneSignalNotificationServiceExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DF9D76ED77CB578563C2573 /* Pods_OneSignalNotificationServiceExtension.framework */; };
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };
D5A301A42D970BAC002A22E9 /* OneSignalNotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D5A3019D2D970BAC002A22E9 /* OneSignalNotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
D5A301A22D970BAC002A22E9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 504EC2FC1FED79650016851F /* Project object */;
proxyType = 1;
remoteGlobalIDString = D5A3019C2D970BAC002A22E9;
remoteInfo = OneSignalNotificationServiceExtension;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
D5A301A92D970BAC002A22E9 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
D5A301A42D970BAC002A22E9 /* OneSignalNotificationServiceExtension.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = "<group>"; };
37F7155EDCE8C061367E30A9 /* Pods-OneSignalNotificationServiceExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OneSignalNotificationServiceExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-OneSignalNotificationServiceExtension/Pods-OneSignalNotificationServiceExtension.debug.xcconfig"; sourceTree = "<group>"; };
4DF9D76ED77CB578563C2573 /* Pods_OneSignalNotificationServiceExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OneSignalNotificationServiceExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; };
504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };
504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
6CB294319AEF8406BACB8AC1 /* Pods-OneSignalNotificationServiceExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OneSignalNotificationServiceExtension.release.xcconfig"; path = "Pods/Target Support Files/Pods-OneSignalNotificationServiceExtension/Pods-OneSignalNotificationServiceExtension.release.xcconfig"; sourceTree = "<group>"; };
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
D5A301982D970B67002A22E9 /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
D5A3019D2D970BAC002A22E9 /* OneSignalNotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = OneSignalNotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
D5A301A52D970BAC002A22E9 /* Exceptions for "OneSignalNotificationServiceExtension" folder in "OneSignalNotificationServiceExtension" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
);
target = D5A3019C2D970BAC002A22E9 /* OneSignalNotificationServiceExtension */;
};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
D5A3019E2D970BAC002A22E9 /* OneSignalNotificationServiceExtension */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
D5A301A52D970BAC002A22E9 /* Exceptions for "OneSignalNotificationServiceExtension" folder in "OneSignalNotificationServiceExtension" target */,
);
explicitFileTypes = {
};
explicitFolders = (
);
path = OneSignalNotificationServiceExtension;
sourceTree = "<group>";
};
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
504EC3011FED79650016851F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D5A3019A2D970BAC002A22E9 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7E144E961F6CA2C63512098E /* Pods_OneSignalNotificationServiceExtension.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
27E2DDA53C4D2A4D1A88CE4A /* Frameworks */ = {
isa = PBXGroup;
children = (
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */,
4DF9D76ED77CB578563C2573 /* Pods_OneSignalNotificationServiceExtension.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
504EC2FB1FED79650016851F = {
isa = PBXGroup;
children = (
504EC3061FED79650016851F /* App */,
D5A3019E2D970BAC002A22E9 /* OneSignalNotificationServiceExtension */,
504EC3051FED79650016851F /* Products */,
7F8756D8B27F46E3366F6CEA /* Pods */,
27E2DDA53C4D2A4D1A88CE4A /* Frameworks */,
);
sourceTree = "<group>";
};
504EC3051FED79650016851F /* Products */ = {
isa = PBXGroup;
children = (
504EC3041FED79650016851F /* App.app */,
D5A3019D2D970BAC002A22E9 /* OneSignalNotificationServiceExtension.appex */,
);
name = Products;
sourceTree = "<group>";
};
504EC3061FED79650016851F /* App */ = {
isa = PBXGroup;
children = (
D5A301982D970B67002A22E9 /* App.entitlements */,
50379B222058CBB4000EE86E /* capacitor.config.json */,
504EC3071FED79650016851F /* AppDelegate.swift */,
504EC30B1FED79650016851F /* Main.storyboard */,
504EC30E1FED79650016851F /* Assets.xcassets */,
504EC3101FED79650016851F /* LaunchScreen.storyboard */,
504EC3131FED79650016851F /* Info.plist */,
2FAD9762203C412B000D30F8 /* config.xml */,
50B271D01FEDC1A000F3C39B /* public */,
);
path = App;
sourceTree = "<group>";
};
7F8756D8B27F46E3366F6CEA /* Pods */ = {
isa = PBXGroup;
children = (
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */,
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */,
37F7155EDCE8C061367E30A9 /* Pods-OneSignalNotificationServiceExtension.debug.xcconfig */,
6CB294319AEF8406BACB8AC1 /* Pods-OneSignalNotificationServiceExtension.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
504EC3031FED79650016851F /* App */ = {
isa = PBXNativeTarget;
buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */;
buildPhases = (
6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */,
504EC3001FED79650016851F /* Sources */,
504EC3011FED79650016851F /* Frameworks */,
504EC3021FED79650016851F /* Resources */,
9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */,
D5A301A92D970BAC002A22E9 /* Embed Foundation Extensions */,
);
buildRules = (
);
dependencies = (
D5A301A32D970BAC002A22E9 /* PBXTargetDependency */,
);
name = App;
productName = App;
productReference = 504EC3041FED79650016851F /* App.app */;
productType = "com.apple.product-type.application";
};
D5A3019C2D970BAC002A22E9 /* OneSignalNotificationServiceExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = D5A301A62D970BAC002A22E9 /* Build configuration list for PBXNativeTarget "OneSignalNotificationServiceExtension" */;
buildPhases = (
D76E39AEACB5B9B2BDC681BF /* [CP] Check Pods Manifest.lock */,
D5A301992D970BAC002A22E9 /* Sources */,
D5A3019A2D970BAC002A22E9 /* Frameworks */,
D5A3019B2D970BAC002A22E9 /* Resources */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
D5A3019E2D970BAC002A22E9 /* OneSignalNotificationServiceExtension */,
);
name = OneSignalNotificationServiceExtension;
productName = OneSignalNotificationServiceExtension;
productReference = D5A3019D2D970BAC002A22E9 /* OneSignalNotificationServiceExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
504EC2FC1FED79650016851F /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1620;
LastUpgradeCheck = 0920;
TargetAttributes = {
504EC3031FED79650016851F = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1100;
ProvisioningStyle = Automatic;
};
D5A3019C2D970BAC002A22E9 = {
CreatedOnToolsVersion = 16.2;
};
};
};
buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */;
compatibilityVersion = "Xcode 8.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 504EC2FB1FED79650016851F;
productRefGroup = 504EC3051FED79650016851F /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
504EC3031FED79650016851F /* App */,
D5A3019C2D970BAC002A22E9 /* OneSignalNotificationServiceExtension */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
504EC3021FED79650016851F /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */,
50B271D11FEDC1A000F3C39B /* public in Resources */,
504EC30F1FED79650016851F /* Assets.xcassets in Resources */,
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,
504EC30D1FED79650016851F /* Main.storyboard in Resources */,
2FAD9763203C412B000D30F8 /* config.xml in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D5A3019B2D970BAC002A22E9 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-App-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-App/Pods-App-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
D76E39AEACB5B9B2BDC681BF /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-OneSignalNotificationServiceExtension-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
504EC3001FED79650016851F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D5A301992D970BAC002A22E9 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
D5A301A32D970BAC002A22E9 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D5A3019C2D970BAC002A22E9 /* OneSignalNotificationServiceExtension */;
targetProxy = D5A301A22D970BAC002A22E9 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
504EC30B1FED79650016851F /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
504EC30C1FED79650016851F /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
504EC3101FED79650016851F /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
504EC3111FED79650016851F /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
504EC3141FED79650016851F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
504EC3151FED79650016851F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
504EC3171FED79650016851F /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = GMCGQ8KK2P;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.0;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = software.federspiel.fedeo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
504EC3181FED79650016851F /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = GMCGQ8KK2P;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = software.federspiel.fedeo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
D5A301A72D970BAC002A22E9 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 37F7155EDCE8C061367E30A9 /* Pods-OneSignalNotificationServiceExtension.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = GMCGQ8KK2P;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = OneSignalNotificationServiceExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = software.federspiel.fedeo.OneSignalNotificationServiceExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
D5A301A82D970BAC002A22E9 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 6CB294319AEF8406BACB8AC1 /* Pods-OneSignalNotificationServiceExtension.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = GMCGQ8KK2P;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = OneSignalNotificationServiceExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = software.federspiel.fedeo.OneSignalNotificationServiceExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */ = {
isa = XCConfigurationList;
buildConfigurations = (
504EC3141FED79650016851F /* Debug */,
504EC3151FED79650016851F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */ = {
isa = XCConfigurationList;
buildConfigurations = (
504EC3171FED79650016851F /* Debug */,
504EC3181FED79650016851F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D5A301A62D970BAC002A22E9 /* Build configuration list for PBXNativeTarget "OneSignalNotificationServiceExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D5A301A72D970BAC002A22E9 /* Debug */,
D5A301A82D970BAC002A22E9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 504EC2FC1FED79650016851F /* Project object */;
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:App.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.security.application-groups</key>
<array>
<string>group.software.federspiel.fedeo.onesignal</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,49 @@
import UIKit
import Capacitor
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
// Called when the app was launched with a url. Feel free to add additional processing here,
// but if you want the App API to support tracking app url opens, make sure to keep this call
return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
}
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
// Called when the app was launched with an activity, including Universal Links.
// Feel free to add additional processing here, but if you want the App API to support
// tracking app url opens, make sure to keep this call
return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -0,0 +1,14 @@
{
"images": [
{
"idiom": "universal",
"size": "1024x1024",
"filename": "AppIcon-512@2x.png",
"platform": "ios"
}
],
"info": {
"author": "xcode",
"version": 1
}
}

View File

@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,56 @@
{
"images": [
{
"idiom": "universal",
"filename": "Default@1x~universal~anyany.png",
"scale": "1x"
},
{
"idiom": "universal",
"filename": "Default@2x~universal~anyany.png",
"scale": "2x"
},
{
"idiom": "universal",
"filename": "Default@3x~universal~anyany.png",
"scale": "3x"
},
{
"appearances": [
{
"appearance": "luminosity",
"value": "dark"
}
],
"idiom": "universal",
"scale": "1x",
"filename": "Default@1x~universal~anyany-dark.png"
},
{
"appearances": [
{
"appearance": "luminosity",
"value": "dark"
}
],
"idiom": "universal",
"scale": "2x",
"filename": "Default@2x~universal~anyany-dark.png"
},
{
"appearances": [
{
"appearance": "luminosity",
"value": "dark"
}
],
"idiom": "universal",
"scale": "3x",
"filename": "Default@3x~universal~anyany-dark.png"
}
],
"info": {
"version": 1,
"author": "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17132" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17105"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<imageView key="view" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Splash" id="snD-IY-ifK">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</imageView>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="Splash" width="1366" height="1366"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14111" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
</dependencies>
<scenes>
<!--Bridge View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="CAPBridgeViewController" customModule="Capacitor" sceneMemberID="viewController"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

63
ios/App/App/Info.plist Normal file
View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>FEDEO</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>fedeo</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSLocationAlwaysUsageDescription</key>
<string>One Signal Notifications</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>One Signal Notifications</string>
<key>NSSupportsLiveActivities</key>
<true/>
<key>NSSupportsLiveActivitiesFrequentUpdates</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,35 @@
import UserNotifications
import OneSignalExtension
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var receivedRequest: UNNotificationRequest!
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.receivedRequest = request
self.contentHandler = contentHandler
self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
/* DEBUGGING: Uncomment the 2 lines below to check this extension is executing
Note, this extension only runs when mutable-content is set
Setting an attachment or action buttons automatically adds this */
// print("Running NotificationServiceExtension")
// bestAttemptContent.body = "[Modified] " + bestAttemptContent.body
OneSignalExtension.didReceiveNotificationExtensionRequest(self.receivedRequest, with: bestAttemptContent, withContentHandler: self.contentHandler)
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
OneSignalExtension.serviceExtensionTimeWillExpireRequest(self.receivedRequest, with: self.bestAttemptContent)
contentHandler(bestAttemptContent)
}
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.software.federspiel.fedeo.onesignal</string>
</array>
</dict>
</plist>

31
ios/App/Podfile Normal file
View File

@@ -0,0 +1,31 @@
require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'
platform :ios, '14.0'
use_frameworks!
# workaround to avoid Xcode caching of Pods that requires
# Product -> Clean Build Folder after new Cordova plugins installed
# Requires CocoaPods 1.6 or newer
install! 'cocoapods', :disable_input_output_paths => true
def capacitor_pods
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorDevice', :path => '../../node_modules/@capacitor/device'
pod 'CapacitorNetwork', :path => '../../node_modules/@capacitor/network'
pod 'CapacitorPluginSafeArea', :path => '../../node_modules/capacitor-plugin-safe-area'
pod 'CordovaPluginsStatic', :path => '../capacitor-cordova-ios-plugins'
end
target 'App' do
capacitor_pods
# Add your Pods here
end
post_install do |installer|
assertDeploymentTarget(installer)
end
target 'OneSignalNotificationServiceExtension' do
pod 'OneSignalXCFramework', '>= 5.0', '< 6.0'
end

99
ios/App/Podfile.lock Normal file
View File

@@ -0,0 +1,99 @@
PODS:
- Capacitor (7.1.0):
- CapacitorCordova
- CapacitorCordova (7.1.0)
- CapacitorDevice (7.0.0):
- Capacitor
- CapacitorNetwork (7.0.0):
- Capacitor
- CapacitorPluginSafeArea (4.0.0):
- Capacitor
- CordovaPluginsStatic (7.1.0):
- CapacitorCordova
- OneSignalXCFramework (= 5.2.10)
- OneSignalXCFramework (5.2.10):
- OneSignalXCFramework/OneSignalComplete (= 5.2.10)
- OneSignalXCFramework/OneSignal (5.2.10):
- OneSignalXCFramework/OneSignalCore
- OneSignalXCFramework/OneSignalExtension
- OneSignalXCFramework/OneSignalLiveActivities
- OneSignalXCFramework/OneSignalNotifications
- OneSignalXCFramework/OneSignalOSCore
- OneSignalXCFramework/OneSignalOutcomes
- OneSignalXCFramework/OneSignalUser
- OneSignalXCFramework/OneSignalComplete (5.2.10):
- OneSignalXCFramework/OneSignal
- OneSignalXCFramework/OneSignalInAppMessages
- OneSignalXCFramework/OneSignalLocation
- OneSignalXCFramework/OneSignalCore (5.2.10)
- OneSignalXCFramework/OneSignalExtension (5.2.10):
- OneSignalXCFramework/OneSignalCore
- OneSignalXCFramework/OneSignalOutcomes
- OneSignalXCFramework/OneSignalInAppMessages (5.2.10):
- OneSignalXCFramework/OneSignalCore
- OneSignalXCFramework/OneSignalNotifications
- OneSignalXCFramework/OneSignalOSCore
- OneSignalXCFramework/OneSignalOutcomes
- OneSignalXCFramework/OneSignalUser
- OneSignalXCFramework/OneSignalLiveActivities (5.2.10):
- OneSignalXCFramework/OneSignalCore
- OneSignalXCFramework/OneSignalOSCore
- OneSignalXCFramework/OneSignalUser
- OneSignalXCFramework/OneSignalLocation (5.2.10):
- OneSignalXCFramework/OneSignalCore
- OneSignalXCFramework/OneSignalNotifications
- OneSignalXCFramework/OneSignalOSCore
- OneSignalXCFramework/OneSignalUser
- OneSignalXCFramework/OneSignalNotifications (5.2.10):
- OneSignalXCFramework/OneSignalCore
- OneSignalXCFramework/OneSignalExtension
- OneSignalXCFramework/OneSignalOutcomes
- OneSignalXCFramework/OneSignalOSCore (5.2.10):
- OneSignalXCFramework/OneSignalCore
- OneSignalXCFramework/OneSignalOutcomes (5.2.10):
- OneSignalXCFramework/OneSignalCore
- OneSignalXCFramework/OneSignalUser (5.2.10):
- OneSignalXCFramework/OneSignalCore
- OneSignalXCFramework/OneSignalNotifications
- OneSignalXCFramework/OneSignalOSCore
- OneSignalXCFramework/OneSignalOutcomes
DEPENDENCIES:
- "Capacitor (from `../../node_modules/@capacitor/ios`)"
- "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
- "CapacitorDevice (from `../../node_modules/@capacitor/device`)"
- "CapacitorNetwork (from `../../node_modules/@capacitor/network`)"
- CapacitorPluginSafeArea (from `../../node_modules/capacitor-plugin-safe-area`)
- CordovaPluginsStatic (from `../capacitor-cordova-ios-plugins`)
- OneSignalXCFramework (< 6.0, >= 5.0)
SPEC REPOS:
trunk:
- OneSignalXCFramework
EXTERNAL SOURCES:
Capacitor:
:path: "../../node_modules/@capacitor/ios"
CapacitorCordova:
:path: "../../node_modules/@capacitor/ios"
CapacitorDevice:
:path: "../../node_modules/@capacitor/device"
CapacitorNetwork:
:path: "../../node_modules/@capacitor/network"
CapacitorPluginSafeArea:
:path: "../../node_modules/capacitor-plugin-safe-area"
CordovaPluginsStatic:
:path: "../capacitor-cordova-ios-plugins"
SPEC CHECKSUMS:
Capacitor: bceb785fb78f5e81e4a9e37843bc1c24bd9c7194
CapacitorCordova: 866217f32c1d25b326c568a10ea3ed0c36b13e29
CapacitorDevice: 069faf433b3a99c3d5f0e500fbe634f60a8c6a84
CapacitorNetwork: 30c2e78a0ed32530656cb426c8ee6c2caec10dbf
CapacitorPluginSafeArea: 22031c3436269ca80fac90ec2c94bc7c1e59a81d
CordovaPluginsStatic: f722d4ff434f50099581e690d579b7c108f490e6
OneSignalXCFramework: 1a3b28dfbff23aabce585796d23c1bef37772774
PODFILE CHECKSUM: ccfbce7f13cfefd953204fe26b280d6431731aa5
COCOAPODS: 1.16.2

View File

@@ -4,6 +4,7 @@
import MainNav from "~/components/MainNav.vue";
import dayjs from "dayjs";
import {useProfileStore} from "~/stores/profile.js";
import {useCapacitor} from "../composables/useCapacitor.js";
const dataStore = useDataStore()
const profileStore = useProfileStore()
@@ -107,14 +108,13 @@ const footerLinks = [/*{
click: () => isHelpSlideoverOpen.value = true
}]
</script>
<template>
<UDashboardLayout v-if="profileStore.loaded">
<UDashboardLayout class="safearea" v-if="profileStore.loaded">
<UDashboardPanel :width="250" :resizable="{ min: 200, max: 300 }" collapsible>
<UDashboardNavbar class="!border-transparent" :ui="{ left: 'flex-1' }">
<UDashboardNavbar style="margin-top: env(safe-area-inset-top, 10px) !important;" :class="['!border-transparent']" :ui="{ left: 'flex-1' }">
<template #left>
<ProfileDropdown class="w-full" />
</template>
@@ -122,20 +122,22 @@ const footerLinks = [/*{
<UDashboardSidebar id="sidebar">
<template #header>
<UDashboardSearchButton label="Suche..."/>
<UDashboardSearchButton v-if="!useCapacitor().getIsPhone()" label="Suche..."/>
</template>
<MainNav/>
<div class="flex-1" />
<UDashboardSidebarLinks :links="footerLinks" />
<UDivider class="sticky bottom-0" />
<template #footer>
<!-- ~/components/UserDropdown.vue -->
<UserDropdown />
<div class="flex flex-col w-full">
<UDashboardSidebarLinks :links="footerLinks" />
<UDivider class="sticky bottom-0" />
<UserDropdown style="margin-bottom: env(safe-area-inset-bottom, 10px) !important;"/>
</div>
</template>
</UDashboardSidebar>
</UDashboardPanel>

241
layouts/mobile.vue Normal file
View File

@@ -0,0 +1,241 @@
<script setup>
import MainNav from "~/components/MainNav.vue";
import dayjs from "dayjs";
import {useProfileStore} from "~/stores/profile.js";
import {useCapacitor} from "../composables/useCapacitor.js";
const dataStore = useDataStore()
const profileStore = useProfileStore()
const colorMode = useColorMode()
const { isHelpSlideoverOpen } = useDashboard()
const supabase = useSupabaseClient()
const router = useRouter()
const route = useRoute()
profileStore.initializeData((await supabase.auth.getUser()).data.user.id)
const month = dayjs().format("MM")
const actions = [
{
id: 'new-customer',
label: 'Kunde hinzufügen',
icon: 'i-heroicons-user-group',
to: "/customers/create" ,
},
{
id: 'new-vendor',
label: 'Lieferant hinzufügen',
icon: 'i-heroicons-truck',
to: "/vendors/create" ,
},
{
id: 'new-contact',
label: 'Ansprechpartner hinzufügen',
icon: 'i-heroicons-user-group',
to: "/contacts/create" ,
},
{
id: 'new-task',
label: 'Aufgabe hinzufügen',
icon: 'i-heroicons-rectangle-stack',
to: "/tasks/create" ,
},
{
id: 'new-plant',
label: 'Objekt hinzufügen',
icon: 'i-heroicons-clipboard-document',
to: "/plants/create" ,
},
{
id: 'new-product',
label: 'Artikel hinzufügen',
icon: 'i-heroicons-puzzle-piece',
to: "/products/create" ,
},
{
id: 'new-project',
label: 'Projekt hinzufügen',
icon: 'i-heroicons-clipboard-document-check',
to: "/projects/create" ,
}
]
const groups = computed(() =>
[{
key: 'actions',
commands: actions
},{
key: "customers",
label: "Kunden",
commands: dataStore.customers.map(item => { return {id: item.id, label: item.name, to: `/customers/show/${item.id}`}})
},{
key: "vendors",
label: "Lieferanten",
commands: dataStore.vendors.map(item => { return {id: item.id, label: item.name, to: `/vendors/show/${item.id}`}})
},{
key: "contacts",
label: "Ansprechpartner",
commands: dataStore.contacts.map(item => { return {id: item.id, label: item.fullName, to: `/contacts/show/${item.id}`}})
},{
key: "products",
label: "Artikel",
commands: dataStore.products.map(item => { return {id: item.id, label: item.name, to: `/products/show/${item.id}`}})
},{
key: "tasks",
label: "Aufgaben",
commands: dataStore.tasks.map(item => { return {id: item.id, label: item.name, to: `/tasks/show/${item.id}`}})
},{
key: "plants",
label: "Objekte",
commands: dataStore.plants.map(item => { return {id: item.id, label: item.name, to: `/plants/show/${item.id}`}})
},{
key: "projects",
label: "Projekte",
commands: dataStore.projects.map(item => { return {id: item.id, label: item.name, to: `/projects/show/${item.id}`}})
}
].filter(Boolean))
const footerLinks = [/*{
label: 'Invite people',
icon: 'i-heroicons-plus',
to: '/settings/members'
}, */{
label: 'Hilfe & Info',
icon: 'i-heroicons-question-mark-circle',
click: () => isHelpSlideoverOpen.value = true
}]
</script>
<template>
<UDashboardLayout class="safearea" v-if="profileStore.loaded">
<UDashboardPanel :width="250" :resizable="{ min: 200, max: 300 }" collapsible>
<UDashboardNavbar style="margin-top: env(safe-area-inset-top, 10px) !important;" :class="['!border-transparent']" :ui="{ left: 'flex-1' }">
<template #left>
<ProfileDropdown class="w-full" />
</template>
</UDashboardNavbar>
<UDashboardSidebar id="sidebar">
<template #header>
<UDashboardSearchButton v-if="!useCapacitor().getIsPhone()" label="Suche..."/>
</template>
<MainNav/>
<div class="flex-1" />
<template #footer>
<div class="flex flex-col w-full">
<UDashboardSidebarLinks :links="footerLinks" />
<UDivider class="sticky bottom-0" />
<UserDropdown style="margin-bottom: env(safe-area-inset-bottom, 10px) !important;"/>
</div>
</template>
</UDashboardSidebar>
</UDashboardPanel>
<UDashboardPage style="height: 90vh">
<UDashboardPanel grow>
<slot />
</UDashboardPanel>
</UDashboardPage>
<div class="mobileFooter bg-white dark:bg-gray-950">
<UButton
icon="i-heroicons-home"
to="/mobile/"
variant="ghost"
:color="route.fullPath === '/mobile' ? 'primary' : 'gray'"
/>
<UButton
icon="i-heroicons-clipboard-document-check"
to="/standardEntity/tasks"
variant="ghost"
:color="route.fullPath === '/standardEntity/tasks' ? 'primary' : 'gray'"
/>
<UButton
icon="i-heroicons-rectangle-stack"
to="/standardEntity/projects"
variant="ghost"
:color="route.fullPath === '/standardEntity/projects' ? 'primary' : 'gray'"
/>
<UButton
icon="i-heroicons-clock"
to="/workingtimes"
variant="ghost"
:color="route.fullPath === '/workingtimes' ? 'primary' : 'gray'"
/>
<UButton
icon="i-heroicons-bars-4"
to="/mobile/menu"
variant="ghost"
:color="route.fullPath === '/mobile/menu' ? 'primary' : 'gray'"
/>
</div>
<!-- ~/components/HelpSlideover.vue -->
<HelpSlideover/>
<!-- ~/components/NotificationsSlideover.vue -->
<NotificationsSlideover />
<ClientOnly>
<LazyUDashboardSearch :groups="groups" hide-color-mode/>
</ClientOnly>
</UDashboardLayout>
<div
v-else
class="flex flex-col"
>
<UColorModeImage
light="/Logo_Hell_Weihnachten.png"
dark="/Logo_Dunkel_Weihnachten.png"
class="w-1/3 mx-auto my-10"
v-if="month === '12'"
/>
<UColorModeImage
light="/Logo.png"
dark="/Logo_Dark.png"
class="w-1/3 mx-auto my-10"
v-else
/>
<div v-if="dataStore.showProfileSelection">
<ProfileSelection/>
</div>
<div v-else>
<UProgress animation="carousel" class="w-3/4 mx-auto mt-10" />
</div>
</div>
</template>
<style scoped>
.mobileFooter {
position: absolute;
bottom: 0;
left: 0;
height: 8vh;
width: 100%;
border-top: 1px solid grey;
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 1em;
}
.mobileFooter > a {
}
</style>

View File

@@ -0,0 +1,7 @@
export default defineNuxtRouteMiddleware(async (to, _from) => {
const router = useRouter()
if(await useCapacitor().getIsPhone()) {
return router.push('/mobile')
}
})

View File

@@ -1,7 +1,7 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: {
enabled: true,
enabled: false,
timeline: {
enabled: true
}
@@ -19,9 +19,6 @@ export default defineNuxtConfig({
components: [{
path: '~/components'
}, {
path: '~/components/common',
pathPrefix: false
}],
build: {

6144
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,6 @@
"postinstall": "nuxt prepare"
},
"devDependencies": {
"@nuxt/devtools": "latest",
"@nuxtjs/leaflet": "^1.2.3",
"@nuxtjs/supabase": "^1.1.4",
"@vite-pwa/nuxt": "^0.3.3",
@@ -21,6 +20,12 @@
"vue-router": "^4.2.5"
},
"dependencies": {
"@capacitor/android": "^7.1.0",
"@capacitor/cli": "^7.1.0",
"@capacitor/core": "^7.1.0",
"@capacitor/device": "^7.0.0",
"@capacitor/ios": "^7.1.0",
"@capacitor/network": "^7.0.0",
"@fullcalendar/core": "^6.1.10",
"@fullcalendar/daygrid": "^6.1.10",
"@fullcalendar/interaction": "^6.1.10",
@@ -48,6 +53,7 @@
"axios": "^1.6.7",
"base64-arraybuffer": "^1.0.2",
"buffer": "^6.0.3",
"capacitor-plugin-safe-area": "^4.0.0",
"chart.js": "^4.4.4",
"client-oauth2": "^4.3.3",
"date-fns": "^3.6.0",
@@ -58,11 +64,13 @@
"maplibre-gl": "^4.7.0",
"nuxt-editorjs": "^1.0.4",
"nuxt-viewport": "^2.0.6",
"onesignal-cordova-plugin": "^5.2.11",
"papaparse": "^5.4.1",
"pdf-lib": "^1.17.1",
"pinia": "^2.1.7",
"sass": "^1.69.7",
"socket.io-client": "^4.7.2",
"tailwindcss-safe-area-capacitor": "^0.5.1",
"uuid": "^11.0.3",
"uuidv4": "^6.2.13",
"v-calendar": "^3.1.2",

View File

@@ -2569,7 +2569,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
:data="uri"
v-if="showDocument"
type="application/pdf"
class="w-full previewDocument"
class="w-full previewDocumentMobile"
/>
</div>
@@ -2598,7 +2598,13 @@ td {
border: 1px solid #69c350;
}*/
.previewDocumentMobile {
aspect-ratio: 1 / 1.414;
}
.previewDocument {
height: 80vh;
}
</style>

View File

@@ -170,22 +170,14 @@ const openEmail = () => {
<UDashboardPanelContent>
<object
:data="linkedDocument.url"
class="h-full"
class="w-full previewDocumentMobile"
/>
</UDashboardPanelContent>
<!-- <DocumentDisplay
:document-data="dataStore.documents.find(i => i.createdDocument === itemInfo.id)"
/>-->
</template>
<style scoped>
.previewDocumentMobile {
aspect-ratio: 1 / 1.414;
}
</style>

View File

@@ -1,64 +1,53 @@
<template>
<UDashboardPage>
<UDashboardPanel grow>
<UDashboardNavbar title="Home">
<template #right>
<UTooltip text="Notifications" :shortcuts="['N']">
<UButton color="gray" variant="ghost" square @click="isNotificationsSlideoverOpen = true">
<UChip :show="unreadMessages" color="primary" inset>
<UIcon name="i-heroicons-bell" class="w-5 h-5" />
</UChip>
</UButton>
</UTooltip>
</template>
</UDashboardNavbar>
<UDashboardNavbar title="Home">
<template #right>
<UTooltip text="Notifications" :shortcuts="['N']">
<UButton color="gray" variant="ghost" square @click="isNotificationsSlideoverOpen = true">
<UChip :show="unreadMessages" color="primary" inset>
<UIcon name="i-heroicons-bell" class="w-5 h-5" />
</UChip>
</UButton>
</UTooltip>
</template>
</UDashboardNavbar>
<UDashboardPanelContent class="flex flex-col">
<div class="mb-5">
<UDashboardCard
title="Einnahmen und Ausgaben(netto)"
class="mt-3"
>
<display-income-and-expenditure/>
</UDashboardCard>
</div>
<UPageGrid>
<UDashboardCard
title="Buchhaltung"
v-if="profileStore.ownTenant.features.accounting"
>
<display-open-balances/>
</UDashboardCard>
<UDashboardCard
title="Projekte"
>
<display-projects-in-phases/>
</UDashboardCard>
<UDashboardCard
title="Anwesenheiten"
>
<display-present-profiles/>
</UDashboardCard>
<UDashboardCard
>
<display-running-time/>
</UDashboardCard>
<UDashboardCard
title="Aufgaben"
>
<display-open-tasks/>
</UDashboardCard>
</UPageGrid>
<div class="flex flex-row">
</div>
</UDashboardPanelContent>
</UDashboardPanel>
</UDashboardPage>
<UDashboardPanelContent>
<div class="mb-5">
<UDashboardCard
title="Einnahmen und Ausgaben(netto)"
class="mt-3"
>
<display-income-and-expenditure/>
</UDashboardCard>
</div>
<UPageGrid>
<UDashboardCard
title="Buchhaltung"
v-if="profileStore.ownTenant.features.accounting"
>
<display-open-balances/>
</UDashboardCard>
<UDashboardCard
title="Projekte"
>
<display-projects-in-phases/>
</UDashboardCard>
<UDashboardCard
title="Anwesenheiten"
>
<display-present-profiles/>
</UDashboardCard>
<UDashboardCard
>
<display-running-time/>
</UDashboardCard>
<UDashboardCard
title="Aufgaben"
>
<display-open-tasks/>
</UDashboardCard>
</UPageGrid>
</UDashboardPanelContent>
</template>
<script setup>
@@ -66,7 +55,7 @@
import DisplayPresentProfiles from "~/components/noAutoLoad/displayPresentProfiles.vue";
definePageMeta({
middleware: "auth"
middleware: ["auth","redirect-to-mobile-index"]
})
const dataStore = useDataStore()
@@ -99,9 +88,5 @@ setup()
</script>
<style scoped>
.card {
border: 1px solid darkgray;
border-radius: 20px;
}
</style>

View File

@@ -1,6 +1,7 @@
<script setup >
import {useProfileStore} from "~/stores/profile.js";
import {useCapacitor} from "~/composables/useCapacitor.js";
definePageMeta({
layout: "notLoggedIn"
@@ -64,7 +65,14 @@ const onSubmit = async (data) => {
} else {
//console.log("Login Successful")
profileStore.initializeData(user.id)
router.push("/")
if(await useCapacitor().getIsPhone()) {
router.push("/mobile")
} else {
router.push("/")
}
}

48
pages/mobile/index.vue Normal file
View File

@@ -0,0 +1,48 @@
<script setup>
import DisplayPresentProfiles from "~/components/noAutoLoad/displayPresentProfiles.vue";
definePageMeta({
layout: 'mobile'
})
const profileStore = useProfileStore()
</script>
<template>
<UDashboardPanelContent>
<UPageGrid>
<UDashboardCard>
<display-welcome/>
</UDashboardCard>
<UDashboardCard
title="Aufgaben"
>
<display-open-tasks/>
</UDashboardCard>
<UDashboardCard
title="Anwesenheit"
>
<display-running-time/>
</UDashboardCard>
<UDashboardCard
title="Buchhaltung"
v-if="profileStore.ownTenant.features.accounting"
>
<display-open-balances/>
</UDashboardCard>
<UDashboardCard
title="Projekte"
>
<display-projects-in-phases/>
</UDashboardCard>
</UPageGrid>
</UDashboardPanelContent>
</template>
<style scoped>
</style>

39
pages/mobile/menu.vue Normal file
View File

@@ -0,0 +1,39 @@
<script setup>
definePageMeta({
layout: 'mobile',
})
</script>
<template>
<UDashboardPanelContent>
<UButton
class="w-full my-1">
Abwesenheiten
</UButton>
<UButton
class="w-full my-1">
Kalender
</UButton>
<UButton
class="w-full my-1">
Kunden
</UButton>
<UButton
class="w-full my-1">
Lieferanten
</UButton>
<UButton
class="w-full my-1">
Objekte
</UButton>
</UDashboardPanelContent>
</template>
<style scoped>
</style>

View File

@@ -1,9 +1,19 @@
<script setup>
import {setPageLayout} from "#app";
import {useCapacitor} from "~/composables/useCapacitor.js";
definePageMeta({
middleware: "auth",
layout: "default",
})
const route = useRoute()
const dataStore = useDataStore()
const supabase = useSupabaseClient()
const type = route.params.type
const platform = await useCapacitor().getIsPhone() ? "mobile" : "default"
console.log(platform)
const dataType = dataStore.dataTypes[route.params.type]
@@ -13,6 +23,11 @@ const items = ref([])
const item = ref({})
const setupPage = async () => {
if(await useCapacitor().getIsPhone()) {
setPageLayout("mobile")
}
if(route.params.mode) mode.value = route.params.mode
if(mode.value === "show") {
@@ -48,16 +63,19 @@ setupPage()
:item="item"
@updateNeeded="setupPage"
:key="item"
:platform="platform"
/>
<EntityEdit
v-else-if="loaded && (mode === 'edit' || mode === 'create')"
:type="route.params.type"
:item="item"
:mode="mode"
/>
<EntityList
v-else-if="loaded && mode === 'list'"
:type="type"
:items="items"
:platform="platform"
/>
<UProgress
v-else

View File

@@ -1,6 +1,9 @@
<script setup>
import dayjs from "dayjs";
import customParseFormat from "dayjs/plugin/customParseFormat"
import {useCapacitor} from "~/composables/useCapacitor.js";
import {setPageLayout} from "#app";
import FloatingActionButton from "~/components/mobile/FloatingActionButton.vue";
dayjs.extend(customParseFormat)
@@ -14,11 +17,18 @@ const supabase = useSupabaseClient()
const router = useRouter()
const route = useRoute()
const platform = useCapacitor().getIsPhone() ? "mobile" : "default"
const filterUser = ref(profileStore.activeProfile.id || "")
const workingtimes = ref([])
const setupPage = async () => {
if(platform === "mobile") {
setPageLayout("mobile")
}
if(route.query) {
if(route.query.profile) filterUser.value = route.query.profile
}
@@ -71,43 +81,35 @@ const itemInfo = ref({
const columns = [
{
key:"state",
label: "Status",
sortable:true
label: "Status"
},
{
key: "approved",
label: "Genehmigt",
sortable:true
label: "Genehmigt"
},
{
key: "profile",
label: "Mitarbeiter",
sortable:true
label: "Mitarbeiter"
},
{
key: "date",
label: "Datum",
sortable:true
label: "Datum"
},
{
key:"startDate",
label:"Start",
sortable:true
label:"Start"
},
{
key: "endDate",
label: "Ende",
sortable:true
label: "Ende"
},
{
key: "duration",
label: "Dauer",
sortable:true
label: "Dauer"
},
{
key: "notes",
label: "Notizen",
sortable:true
label: "Notizen"
}
]
@@ -155,7 +157,21 @@ const setEndDate = (row) => {
</script>
<template>
<UDashboardNavbar title="Anwesenheiten">
<FloatingActionButton
:label="`+ Anwesenheit`"
variant="outline"
v-if="platform === 'mobile'"
@click="router.push(`/workingtimes/create`)"
:pos="0"
/>
<FloatingActionButton
:label="`Auswertung`"
variant="outline"
v-if="platform === 'mobile'"
@click="router.push(`/workingtimes/evaluate/${profileStore.activeProfile.id}`)"
:pos="1"
/>
<UDashboardNavbar title="Anwesenheiten" v-if="platform !== 'mobile'">
<template #right>
<UButton
@click="router.push(`/workingtimes/edit?profile=${filterUser}`)"
@@ -164,7 +180,12 @@ const setEndDate = (row) => {
</UButton>
</template>
</UDashboardNavbar>
<UDashboardToolbar>
<UDashboardNavbar title="Anwesenheiten" v-else>
<template #toggle>
<div></div>
</template>
</UDashboardNavbar>
<UDashboardToolbar v-if="platform !== 'mobile'">
<template #left>
<USelectMenu

View File

@@ -822,7 +822,7 @@ export const useDataStore = defineStore('data', () => {
redirect:true,
historyItemHolder: "project",
numberRangeHolder: "projectNumber",
supabaseSelectWithInformation: "*, customer(id,name), plant(id,name), projecttype(name, id), tasks(*), files(*), createddocuments(*), events(*)",
supabaseSelectWithInformation: "*, customer(id,name), plant(id,name), projecttype(name, id), tasks(*, project(id,name), customer(id,name), plant(id,name)), files(*), createddocuments(*, statementallocations(*)), events(*)",
supabaseSortColumn: "projectNumber",
filters: [
{

View File

@@ -1,5 +1,6 @@
import {defineStore} from 'pinia'
import OneSignal from "onesignal-cordova-plugin";
import {Capacitor} from "@capacitor/core";
// @ts-ignore
export const useProfileStore = defineStore('profile', () => {
@@ -44,6 +45,14 @@ export const useProfileStore = defineStore('profile', () => {
activeProfile.value = profiles.find(i => i.id === activeProfileConnection.profile_id)
currentTenant.value = activeProfile.value.tenant
if(Capacitor.getPlatform() === "ios") {
OneSignal.initialize("1295d5ff-28f8-46a6-9c62-fe5d090016d7");
OneSignal.Location.setShared(false)
OneSignal.Notifications.requestPermission();
OneSignal.login(activeProfileConnection.user_id)
}
await fetchData()
await dataStore.fetchData()
} else {

View File

@@ -21,5 +21,8 @@ export default <Partial<Config>>{
}
}
}
}
},
plugins: [
require('tailwindcss-safe-area-capacitor'),
]
}