Skip to content

Commit

Permalink
Feature: Desktop-View - Implement Notifications section
Browse files Browse the repository at this point in the history
  • Loading branch information
Benjamin Scharf committed May 16, 2024
1 parent aea3761 commit b72e5ce
Show file tree
Hide file tree
Showing 51 changed files with 3,188 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<td><%- @P(group, 'name') %>
<td class="u-positionOrigin">
<label class="checkbox-replacement checkbox-replacement--fullscreen">
<input type="checkbox" name="group_ids" value="<%= group.id %>" <% if _.include(@config.group_ids, group.id.toString()): %>checked<% end %>/>
<input type="checkbox" name="group_ids" value="<%= group.id %>" <% if _.include(_.map(@config.group_ids, (group_id) -> group_id.toString()), group.id.toString()): %>checked<% end %>/>
<%- @Icon('checkbox', 'icon-unchecked') %>
<%- @Icon('checkbox-checked', 'icon-checked') %>
</label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,29 @@ export interface Props {
}
defineProps<Props>()
// :INFO - This would only would work on runtime, when keys are computed
// :TODO - Find a way to infer the types on compile time or remove it completely
// defineSlots<{
// [key: `column-cell-${TableHeader['key']}`]: [
// { item: TableHeader; header: TableHeader },
// ]
// [key: `header-suffix-${TableHeader['key']}`]: [{ item: TableHeader }]
// [key: `item-suffix-${TableHeader['key']}`]: [{ item: TableHeader }]
// test: []
// }>()
// Styling
const cellAlignmentClasses = {
right: 'text-right',
center: 'text-center',
left: 'text-left',
}
const rowBackgroundClasses = 'bg-blue-200 dark:bg-gray-700'
const columnSeparatorClasses =
'border-r border-neutral-100 dark:border-gray-900'
</script>

<template>
Expand All @@ -21,9 +44,14 @@ defineProps<Props>()
v-for="header in headers"
:key="header.key"
class="h-10 p-2.5 text-xs font-normal text-stone-200 ltr:text-left rtl:text-right dark:text-neutral-500"
:class="[
header.columnClass,
header.columnSeparator && columnSeparatorClasses,
]"
>
<CommonLabel
class="font-normal text-stone-200 dark:text-neutral-500"
:class="[cellAlignmentClasses[header.alignContent || 'left']]"
size="small"
>{{
$t(header.label, ...(header.labelPlaceholder || []))
Expand All @@ -46,17 +74,27 @@ defineProps<Props>()
v-for="header in headers"
:key="`${item.id}-${header.key}`"
class="h-10 p-2.5 text-sm text-gray-100 first:rounded-s-md last:rounded-e-md dark:text-neutral-400"
:class="{ 'bg-blue-200 dark:bg-gray-700': (index + 1) % 2 }"
:class="[
(index + 1) % 2 && rowBackgroundClasses,
header.columnSeparator && columnSeparatorClasses,
cellAlignmentClasses[header.alignContent || 'left'],
]"
>
<CommonLabel class="text-black dark:text-white">
<template v-if="!item[header.key]">-</template>
<template v-else-if="header.type === 'timestamp'">
<CommonDateTime :date-time="item[header.key] as string" />
</template>
<template v-else>
{{ item[header.key] }}
</template>
</CommonLabel>
<slot
:name="`column-cell-${header.key}`"
:item="item"
:header="header"
>
<CommonLabel class="text-black dark:text-white">
<template v-if="!item[header.key]">-</template>
<template v-else-if="header.type === 'timestamp'">
<CommonDateTime :date-time="item[header.key] as string" />
</template>
<template v-else>
{{ item[header.key] }}
</template>
</CommonLabel>
</slot>

<slot :name="`item-suffix-${header.key}`" :item="item" />
</td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ const tableActions: MenuItem[] = [

const renderTable = (props: Props, options = {}) => {
return renderComponent(CommonSimpleTable, {
shallow: false,
...options,
props,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class="h-10 p-2.5 text-xs font-normal text-stone-200 ltr:text-left rtl:text-right dark:text-neutral-500"
>
<common-label-stub
class="font-normal text-stone-200 dark:text-neutral-500"
class="font-normal text-stone-200 dark:text-neutral-500 text-left"
size="small"
/>

Expand All @@ -17,7 +17,7 @@
class="h-10 p-2.5 text-xs font-normal text-stone-200 ltr:text-left rtl:text-right dark:text-neutral-500"
>
<common-label-stub
class="font-normal text-stone-200 dark:text-neutral-500"
class="font-normal text-stone-200 dark:text-neutral-500 text-left"
size="small"
/>

Expand All @@ -38,24 +38,28 @@
<tr>

<td
class="h-10 p-2.5 text-sm text-gray-100 first:rounded-s-md last:rounded-e-md dark:text-neutral-400 bg-blue-200 dark:bg-gray-700"
class="h-10 p-2.5 text-sm text-gray-100 first:rounded-s-md last:rounded-e-md dark:text-neutral-400 bg-blue-200 dark:bg-gray-700 text-left"
>

<common-label-stub
class="text-black dark:text-white"
size="medium"
/>



</td>
<td
class="h-10 p-2.5 text-sm text-gray-100 first:rounded-s-md last:rounded-e-md dark:text-neutral-400 bg-blue-200 dark:bg-gray-700"
class="h-10 p-2.5 text-sm text-gray-100 first:rounded-s-md last:rounded-e-md dark:text-neutral-400 bg-blue-200 dark:bg-gray-700 text-left"
>

<common-label-stub
class="text-black dark:text-white"
size="medium"
/>



</td>

<td
Expand Down
13 changes: 9 additions & 4 deletions app/frontend/apps/desktop/components/CommonSimpleTable/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/

export interface TableHeader {
key: string
type TableColumnType = 'timestamp'

export interface TableHeader<K = string> {
key: K
label: string
labelPlaceholder?: string[]
type?: 'timestamp'
columnClass?: string
columnSeparator?: boolean
alignContent?: 'center' | 'right'
type?: TableColumnType
[key: string]: unknown
}

export interface TableItem {
[key: string]: unknown
id: string | number
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->

<script setup lang="ts">
import { toRef } from 'vue'
import { cloneDeep } from 'lodash-es'
import CommonSimpleTable from '#desktop/components/CommonSimpleTable/CommonSimpleTable.vue'
import useValue from '#shared/components/Form/composables/useValue.ts'
import type { FormFieldContext } from '#shared/components/Form/types/field.ts'
import type { TableHeader } from '#desktop/components/CommonSimpleTable/types.ts'
import {
NotificationMatrixColumnKey,
NotificationMatrixPathKey,
NotificationMatrixRowKey,
} from './types.ts'
const props = defineProps<{
context: FormFieldContext
}>()
const context = toRef(props, 'context')
const { localValue } = useValue(context)
const tableHeaders: TableHeader[] = [
{
key: 'name',
label: __('Name'),
},
{
key: NotificationMatrixColumnKey.MyTickets,
path: NotificationMatrixPathKey.Criteria,
label: __('My tickets'),
alignContent: 'center',
columnClass: 'w-20',
},
{
key: NotificationMatrixColumnKey.NotAssigned,
path: NotificationMatrixPathKey.Criteria,
label: __('Not assigned'),
alignContent: 'center',
columnClass: 'w-20',
},
{
key: NotificationMatrixColumnKey.SubscribedTickets,
path: NotificationMatrixPathKey.Criteria,
label: __('Subscribed tickets'),
alignContent: 'center',
columnClass: 'w-20',
},
{
key: NotificationMatrixColumnKey.AllTickets,
path: NotificationMatrixPathKey.Criteria,
label: __('All tickets'),
alignContent: 'center',
columnClass: 'w-20',
columnSeparator: true,
},
{
key: NotificationMatrixColumnKey.AlsoNotifyViaEmail,
path: NotificationMatrixPathKey.Channel,
label: __('Also notify via email'),
alignContent: 'center',
columnClass: 'w-20',
},
]
const tableItems = [
{
id: 1,
key: NotificationMatrixRowKey.Create,
name: __('New ticket'),
},
{
id: 2,
key: NotificationMatrixRowKey.Update,
name: __('Ticket update'),
},
{
id: 3,
key: NotificationMatrixRowKey.ReminderReached,
name: __('Ticket reminder reached'),
},
{
id: 4,
key: NotificationMatrixRowKey.Escalation,
name: __('Ticket escalation'),
},
]
const valueLookup = (
rowKey: NotificationMatrixRowKey,
pathKey: NotificationMatrixPathKey,
columnKey: NotificationMatrixColumnKey,
) => {
const row = localValue.value?.[rowKey]
if (!row) return undefined
return row[pathKey]?.[columnKey]
}
const updateValue = (
rowKey: NotificationMatrixRowKey,
pathKey: NotificationMatrixPathKey,
columnKey: NotificationMatrixColumnKey,
state: boolean | undefined,
) => {
const values = cloneDeep(localValue.value) || {}
values[rowKey] = values[rowKey] || {}
values[rowKey][pathKey] = values[rowKey][pathKey] || {}
values[rowKey][pathKey][columnKey] = Boolean(state)
localValue.value = values
}
</script>

<template>
<output
:id="context.id"
:class="context.classes.input"
:name="context.node.name"
:aria-disabled="context.disabled"
:aria-describedby="context.describedBy"
v-bind="context.attrs"
>
<CommonSimpleTable
class="mb-4 w-full"
:headers="tableHeaders"
:items="tableItems"
>
<template
v-for="key in NotificationMatrixColumnKey"
:key="key"
#[`column-cell-${key}`]="{ item, header }"
>
<FormKit
:id="`notifications_${item.key}_${header.path}_${header.key}`"
:model-value="
valueLookup(
item.key as NotificationMatrixRowKey,
header.path as NotificationMatrixPathKey,
header.key as NotificationMatrixColumnKey,
)
"
type="checkbox"
:name="`notifications_${item.key}_${header.path}_${header.key}`"
:disabled="context.disabled"
:ignore="true"
:label-sr-only="true"
:label="`${i18n.t(item.name as string)} - ${i18n.t(header.label)}`"
@update:model-value="
updateValue(
item.key as NotificationMatrixRowKey,
header.path as NotificationMatrixPathKey,
header.key as NotificationMatrixColumnKey,
$event,
)
"
@blur="context.handlers.blur"
/>
</template>
</CommonSimpleTable>
</output>
</template>

0 comments on commit b72e5ce

Please sign in to comment.