After Width: | Height: | Size: 257 KiB |
After Width: | Height: | Size: 91 KiB |
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 603 KiB |
After Width: | Height: | Size: 279 KiB |
After Width: | Height: | Size: 940 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 652 B |
Before Width: | Height: | Size: 570 B After Width: | Height: | Size: 620 B |
After Width: | Height: | Size: 652 B |
After Width: | Height: | Size: 682 B |
@ -0,0 +1,46 @@ |
|||||||
|
<script setup lang="ts"> |
||||||
|
const props = defineProps<{ |
||||||
|
visible?: boolean |
||||||
|
saving?: boolean |
||||||
|
}>() |
||||||
|
|
||||||
|
const emit = defineEmits(['submit', 'cancel', 'update:visible']) |
||||||
|
|
||||||
|
const visible = useVModel(props, 'visible', emit) |
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<GeneralModal v-model:visible="visible" size="small"> |
||||||
|
<div class="flex flex-col p-6" @click.stop> |
||||||
|
<div class="flex flex-row pb-2 mb-4 font-medium text-lg border-b-1 border-gray-50 text-gray-800">Field Type Change</div> |
||||||
|
|
||||||
|
<div class="mb-3 text-gray-800"> |
||||||
|
<div class="flex item-center gap-2 mb-4"> |
||||||
|
<component :is="iconMap.warning" id="nc-selected-item-icon" class="text-yellow-500 w-10 h-10" /> |
||||||
|
This action cannot be undone. Converting data types may result in data loss. Proceed with caution! |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<slot name="entity-preview"></slot> |
||||||
|
|
||||||
|
<div class="flex flex-row gap-x-2 mt-2.5 pt-2.5 justify-end"> |
||||||
|
<NcButton type="secondary" @click="visible = false"> |
||||||
|
{{ $t('general.cancel') }} |
||||||
|
</NcButton> |
||||||
|
|
||||||
|
<NcButton |
||||||
|
key="submit" |
||||||
|
autofocus |
||||||
|
type="primary" |
||||||
|
html-type="submit" |
||||||
|
:loading="saving" |
||||||
|
data-testid="nc-delete-modal-delete-btn" |
||||||
|
@click="emit('submit')" |
||||||
|
> |
||||||
|
Update |
||||||
|
<template #loading> Saving... </template> |
||||||
|
</NcButton> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</GeneralModal> |
||||||
|
</template> |
@ -0,0 +1,197 @@ |
|||||||
|
<script lang="ts" setup> |
||||||
|
import tinycolor from 'tinycolor2' |
||||||
|
import windiColors from 'windicss/colors' |
||||||
|
import { themeV3Colors } from '../../utils/colorsUtils' |
||||||
|
|
||||||
|
interface Props { |
||||||
|
modelValue?: string | any |
||||||
|
isOpen?: boolean |
||||||
|
} |
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), { |
||||||
|
isOpen: false, |
||||||
|
}) |
||||||
|
|
||||||
|
const emit = defineEmits(['input', 'closeModal']) |
||||||
|
|
||||||
|
const { isOpen } = toRefs(props) |
||||||
|
|
||||||
|
const vModel = computed({ |
||||||
|
get: () => props.modelValue, |
||||||
|
set: (val) => { |
||||||
|
emit('input', val || null) |
||||||
|
}, |
||||||
|
}) |
||||||
|
|
||||||
|
const showActiveColorTab = ref<boolean>(false) |
||||||
|
|
||||||
|
const picked = ref<string>(props.modelValue || enumColor.light[0]) |
||||||
|
|
||||||
|
const defaultColors = computed<string[][]>(() => { |
||||||
|
const colors = [ |
||||||
|
'gray', |
||||||
|
'red', |
||||||
|
'green', |
||||||
|
'yellow', |
||||||
|
'orange', |
||||||
|
'pink', |
||||||
|
'maroon', |
||||||
|
'purple', |
||||||
|
'blue', |
||||||
|
] as (keyof typeof themeV3Colors)[] & (keyof typeof windiColors)[] |
||||||
|
|
||||||
|
const allColors = [] |
||||||
|
|
||||||
|
for (const color of colors) { |
||||||
|
if (themeV3Colors[color]) { |
||||||
|
allColors.push(color === 'gray' ? Object.values(themeV3Colors[color]).slice(1) : Object.values(themeV3Colors[color])) |
||||||
|
} else if (windiColors[color]) { |
||||||
|
allColors.push(Object.values(windiColors[color])) |
||||||
|
} |
||||||
|
} |
||||||
|
return allColors |
||||||
|
}) |
||||||
|
|
||||||
|
const localIsDefaultColorTab = ref<'true' | 'false'>('true') |
||||||
|
|
||||||
|
const isDefaultColorTab = computed({ |
||||||
|
get: () => { |
||||||
|
if (showActiveColorTab.value && vModel.value) { |
||||||
|
for (const colorGrp of defaultColors.value) { |
||||||
|
if (colorGrp.includes(vModel.value)) { |
||||||
|
return 'true' |
||||||
|
} |
||||||
|
} |
||||||
|
return 'false' |
||||||
|
} |
||||||
|
|
||||||
|
return localIsDefaultColorTab.value |
||||||
|
}, |
||||||
|
set: (val: 'true' | 'false') => { |
||||||
|
localIsDefaultColorTab.value = val |
||||||
|
|
||||||
|
if (showActiveColorTab.value) { |
||||||
|
showActiveColorTab.value = false |
||||||
|
} |
||||||
|
}, |
||||||
|
}) |
||||||
|
|
||||||
|
const selectColor = (color: string, closeModal = false) => { |
||||||
|
picked.value = color |
||||||
|
|
||||||
|
if (closeModal) { |
||||||
|
emit('closeModal') |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const compare = (colorA: string, colorB: string) => { |
||||||
|
if (!colorA || !colorB) return false |
||||||
|
|
||||||
|
return colorA.toLowerCase() === colorB.toLowerCase() || colorA.toLowerCase() === tinycolor(colorB).toHex8String().toLowerCase() |
||||||
|
} |
||||||
|
|
||||||
|
watch(picked, (n, _o) => { |
||||||
|
vModel.value = n |
||||||
|
}) |
||||||
|
|
||||||
|
watch( |
||||||
|
isOpen, |
||||||
|
(newValue) => { |
||||||
|
if (newValue) { |
||||||
|
showActiveColorTab.value = true |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
immediate: true, |
||||||
|
}, |
||||||
|
) |
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<div class="nc-advance-color-picker w-[336px] pt-2" click.stop> |
||||||
|
<NcTabs v-model:activeKey="isDefaultColorTab" class="nc-advance-color-picker-tab w-full"> |
||||||
|
<a-tab-pane key="true"> |
||||||
|
<template #tab> |
||||||
|
<div class="tab" data-testid="nc-default-colors-tab">Default colors</div> |
||||||
|
</template> |
||||||
|
<div class="h-full p-2"> |
||||||
|
<div class="flex flex-col gap-1"> |
||||||
|
<div v-for="(colorGroup, i) of defaultColors" :key="i" class="flex"> |
||||||
|
<div v-for="(color, j) of colorGroup" :key="`color-${i}-${j}`" class="p-1 rounded-md flex h-8 hover:bg-gray-200"> |
||||||
|
<button |
||||||
|
class="color-selector" |
||||||
|
:class="{ selected: compare(picked, color) }" |
||||||
|
:style="{ |
||||||
|
backgroundColor: `${color}`, |
||||||
|
}" |
||||||
|
@click="selectColor(color, true)" |
||||||
|
></button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</a-tab-pane> |
||||||
|
<a-tab-pane key="false"> |
||||||
|
<template #tab> |
||||||
|
<div class="tab" data-testid="nc-custom-colors-tab"> |
||||||
|
<div>Custom colours</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
<div class="h-full p-2"> |
||||||
|
<LazyGeneralChromeWrapper v-model="picked" class="!w-full !shadow-none" /> |
||||||
|
</div> |
||||||
|
</a-tab-pane> |
||||||
|
</NcTabs> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<style lang="scss" scoped> |
||||||
|
.color-picker { |
||||||
|
@apply flex flex-col items-center justify-center bg-white p-2.5; |
||||||
|
} |
||||||
|
.color-picker-row { |
||||||
|
@apply flex flex-row space-x-1; |
||||||
|
} |
||||||
|
.color-selector { |
||||||
|
@apply h-6 w-6 rounded; |
||||||
|
-webkit-text-stroke-width: 1px; |
||||||
|
-webkit-text-stroke-color: white; |
||||||
|
} |
||||||
|
.color-selector:hover { |
||||||
|
filter: brightness(90%); |
||||||
|
-webkit-filter: brightness(90%); |
||||||
|
} |
||||||
|
.color-selector:focus, |
||||||
|
.color-selector.selected, |
||||||
|
.nc-more-colors-trigger:focus { |
||||||
|
outline: none; |
||||||
|
box-shadow: 0px 0px 0px 2px #fff, 0px 0px 0px 4px #3069fe; |
||||||
|
} |
||||||
|
|
||||||
|
:deep(.vc-chrome-toggle-icon) { |
||||||
|
@apply !ml-3; |
||||||
|
} |
||||||
|
|
||||||
|
:deep(.ant-tabs) { |
||||||
|
@apply !overflow-visible; |
||||||
|
.ant-tabs-nav { |
||||||
|
@apply px-1; |
||||||
|
.ant-tabs-nav-list { |
||||||
|
@apply w-[99%] mx-auto gap-6; |
||||||
|
|
||||||
|
.ant-tabs-tab { |
||||||
|
@apply flex-1 flex items-center justify-center pt-2 pb-2 text-xs font-semibold; |
||||||
|
|
||||||
|
& + .ant-tabs-tab { |
||||||
|
@apply !ml-0; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
.ant-tabs-content-holder { |
||||||
|
.ant-tabs-content { |
||||||
|
@apply h-full; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -1,17 +0,0 @@ |
|||||||
<script setup lang="ts"> |
|
||||||
import type { ProjectEventType } from 'nocodb-sdk' |
|
||||||
|
|
||||||
const props = defineProps<{ |
|
||||||
item: ProjectEventType |
|
||||||
}>() |
|
||||||
|
|
||||||
const item = toRef(props, 'item') |
|
||||||
</script> |
|
||||||
|
|
||||||
<template> |
|
||||||
<NotificationItemWrapper :item="item"> |
|
||||||
<div class="text-xs"> |
|
||||||
<strong>{{ item.data?.user?.name }}</strong> {{ item.data?.action }} <strong>{{ item.data?.table?.name }}</strong> |
|
||||||
</div> |
|
||||||
</NotificationItemWrapper> |
|
||||||
</template> |
|
@ -1,17 +0,0 @@ |
|||||||
<script setup lang="ts"> |
|
||||||
import type { ProjectEventType } from 'nocodb-sdk' |
|
||||||
|
|
||||||
const props = defineProps<{ |
|
||||||
item: ProjectEventType |
|
||||||
}>() |
|
||||||
|
|
||||||
const item = toRef(props, 'item') |
|
||||||
</script> |
|
||||||
|
|
||||||
<template> |
|
||||||
<NotificationItemWrapper :item="item"> |
|
||||||
<div class="text-xs"> |
|
||||||
<strong>{{ item.data?.user?.name }}</strong> {{ item.data?.action }} <strong>{{ item.data?.table?.name }}</strong> |
|
||||||
</div> |
|
||||||
</NotificationItemWrapper> |
|
||||||
</template> |
|
@ -1,38 +0,0 @@ |
|||||||
<script setup lang="ts"> |
|
||||||
import type { ProjectEventType } from 'nocodb-sdk' |
|
||||||
import { AppEvents } from 'nocodb-sdk' |
|
||||||
|
|
||||||
const props = defineProps<{ |
|
||||||
item: ProjectEventType |
|
||||||
}>() |
|
||||||
|
|
||||||
const item = toRef(props, 'item') |
|
||||||
|
|
||||||
const { navigateToProject } = useGlobal() |
|
||||||
|
|
||||||
const action = computed(() => { |
|
||||||
switch (item.value.type) { |
|
||||||
case AppEvents.PROJECT_CREATE: |
|
||||||
return 'created' |
|
||||||
case AppEvents.PROJECT_UPDATE: |
|
||||||
return 'updated' |
|
||||||
case AppEvents.PROJECT_DELETE: |
|
||||||
return 'deleted' |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
const onClick = () => { |
|
||||||
if (item.value.type === AppEvents.PROJECT_DELETE) return |
|
||||||
navigateToProject({ baseId: item.value.body.id }) |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<template> |
|
||||||
<NotificationItemWrapper :item="item" @click="onClick"> |
|
||||||
<div class="text-xs gap-2"> |
|
||||||
Base |
|
||||||
<GeneralProjectIcon style="vertical-align: middle" :type="item.body.type" /> <strong>{{ item.body.title }}</strong> |
|
||||||
{{ action }} successfully |
|
||||||
</div> |
|
||||||
</NotificationItemWrapper> |
|
||||||
</template> |
|
@ -1,38 +0,0 @@ |
|||||||
<script setup lang="ts"> |
|
||||||
import type { ProjectEventType } from 'nocodb-sdk' |
|
||||||
import { AppEvents } from 'nocodb-sdk' |
|
||||||
|
|
||||||
const props = defineProps<{ |
|
||||||
item: ProjectEventType |
|
||||||
}>() |
|
||||||
|
|
||||||
const item = toRef(props, 'item') |
|
||||||
|
|
||||||
const { navigateToProject } = useGlobal() |
|
||||||
|
|
||||||
const action = computed(() => { |
|
||||||
switch (item.value.type) { |
|
||||||
case AppEvents.VIEW_CREATE: |
|
||||||
return 'created' |
|
||||||
case AppEvents.VIEW_UPDATE: |
|
||||||
return 'updated' |
|
||||||
case AppEvents.VIEW_DELETE: |
|
||||||
return 'deleted' |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
const onClick = () => { |
|
||||||
if (item.value.type === AppEvents.VIEW_DELETE) return |
|
||||||
navigateToProject({ baseId: item.value.body.id }) |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<template> |
|
||||||
<NotificationItemWrapper :item="item" @click="onClick"> |
|
||||||
<div class="text-xs gap-2"> |
|
||||||
Shared view |
|
||||||
<strong>{{ item.body.title }}</strong> |
|
||||||
{{ action }} successfully |
|
||||||
</div> |
|
||||||
</NotificationItemWrapper> |
|
||||||
</template> |
|
@ -1,17 +0,0 @@ |
|||||||
<script setup lang="ts"> |
|
||||||
import type { ProjectEventType } from 'nocodb-sdk' |
|
||||||
|
|
||||||
const props = defineProps<{ |
|
||||||
item: ProjectEventType |
|
||||||
}>() |
|
||||||
|
|
||||||
const item = toRef(props, 'item') |
|
||||||
</script> |
|
||||||
|
|
||||||
<template> |
|
||||||
<NotificationItemWrapper :item="item"> |
|
||||||
<div class="text-xs"> |
|
||||||
<strong>{{ item.data?.user?.name }}</strong> {{ item.data?.action }} <strong>{{ item.data?.table?.name }}</strong> |
|
||||||
</div> |
|
||||||
</NotificationItemWrapper> |
|
||||||
</template> |
|
@ -1,38 +0,0 @@ |
|||||||
<script setup lang="ts"> |
|
||||||
import type { TableEventType } from 'nocodb-sdk' |
|
||||||
import { AppEvents } from 'nocodb-sdk' |
|
||||||
|
|
||||||
const props = defineProps<{ |
|
||||||
item: TableEventType |
|
||||||
}>() |
|
||||||
|
|
||||||
const item = toRef(props, 'item') |
|
||||||
|
|
||||||
const { navigateToProject } = useGlobal() |
|
||||||
|
|
||||||
const action = computed(() => { |
|
||||||
switch (item.value.type) { |
|
||||||
case AppEvents.TABLE_CREATE: |
|
||||||
return 'created' |
|
||||||
case AppEvents.TABLE_UPDATE: |
|
||||||
return 'updated' |
|
||||||
case AppEvents.TABLE_DELETE: |
|
||||||
return 'deleted' |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
const onClick = () => { |
|
||||||
if (item.value.type === AppEvents.TABLE_DELETE) return |
|
||||||
navigateToProject({ baseId: item.value.body.id }) |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<template> |
|
||||||
<NotificationItemWrapper :item="item" @click="onClick"> |
|
||||||
<div class="text-xs gap-2"> |
|
||||||
Table |
|
||||||
<strong>{{ item.body.title }}</strong> |
|
||||||
{{ action }} successfully |
|
||||||
</div> |
|
||||||
</NotificationItemWrapper> |
|
||||||
</template> |
|
@ -1,38 +0,0 @@ |
|||||||
<script setup lang="ts"> |
|
||||||
import type { ProjectEventType } from 'nocodb-sdk' |
|
||||||
import { AppEvents } from 'nocodb-sdk' |
|
||||||
|
|
||||||
const props = defineProps<{ |
|
||||||
item: ProjectEventType |
|
||||||
}>() |
|
||||||
|
|
||||||
const item = toRef(props, 'item') |
|
||||||
|
|
||||||
const { navigateToProject } = useGlobal() |
|
||||||
|
|
||||||
const action = computed(() => { |
|
||||||
switch (item.value.type) { |
|
||||||
case AppEvents.VIEW_CREATE: |
|
||||||
return 'created' |
|
||||||
case AppEvents.VIEW_UPDATE: |
|
||||||
return 'updated' |
|
||||||
case AppEvents.VIEW_DELETE: |
|
||||||
return 'deleted' |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
const onClick = () => { |
|
||||||
if (item.value.type === AppEvents.VIEW_DELETE) return |
|
||||||
navigateToProject({ baseId: item.value.body.id }) |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<template> |
|
||||||
<NotificationItemWrapper :item="item" @click="onClick"> |
|
||||||
<div class="text-xs gap-2"> |
|
||||||
View |
|
||||||
<strong>{{ item.body.title }}</strong> |
|
||||||
{{ action }} successfully |
|
||||||
</div> |
|
||||||
</NotificationItemWrapper> |
|
||||||
</template> |
|
@ -1,26 +1,15 @@ |
|||||||
<script setup lang="ts"> |
<script setup lang="ts"> |
||||||
|
import type { WelcomeEventType } from 'nocodb-sdk' |
||||||
|
|
||||||
const props = defineProps<{ |
const props = defineProps<{ |
||||||
item: any |
item: WelcomeEventType |
||||||
}>() |
}>() |
||||||
|
|
||||||
const router = useRouter() |
|
||||||
const route = router.currentRoute |
|
||||||
|
|
||||||
const item = toRef(props, 'item') |
const item = toRef(props, 'item') |
||||||
|
|
||||||
const navigateToHome = () => { |
|
||||||
if (route.value.path !== '/') { |
|
||||||
navigateTo(`/`) |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
</script> |
||||||
|
|
||||||
<template> |
<template> |
||||||
<NotificationItemWrapper :item="item" @click="navigateToHome"> |
<NotificationItemWrapper :item="item"> |
||||||
<template #avatar> |
<div>Welcome to <span class="font-semibold">NocoDB!</span> We’re excited to have you onboard.</div> |
||||||
<img src="~/assets/img/icons/64x64.png" class="w-6" /> |
|
||||||
</template> |
|
||||||
|
|
||||||
<div class="text-xs">Welcome to <strong>NocoHUB!</strong> We’re excited to have you onboard.</div> |
|
||||||
</NotificationItemWrapper> |
</NotificationItemWrapper> |
||||||
</template> |
</template> |
||||||
|
@ -1,94 +1,73 @@ |
|||||||
<script setup lang="ts"> |
<script setup lang="ts"> |
||||||
import { timeAgo } from 'nocodb-sdk' |
import type { NotificationType } from 'nocodb-sdk' |
||||||
|
import { timeAgo } from '~/utils/datetimeUtils' |
||||||
|
|
||||||
const props = defineProps<{ |
const props = defineProps<{ |
||||||
item: { |
item: NotificationType |
||||||
created_at: any |
|
||||||
} |
|
||||||
}>() |
}>() |
||||||
|
|
||||||
const item = toRef(props, 'item') |
const item = toRef(props, 'item') |
||||||
|
|
||||||
|
const { isMobileMode } = useGlobal() |
||||||
|
|
||||||
const notificationStore = useNotification() |
const notificationStore = useNotification() |
||||||
|
|
||||||
const { markAsRead } = notificationStore |
const { toggleRead, deleteNotification } = notificationStore |
||||||
</script> |
</script> |
||||||
|
|
||||||
<template> |
<template> |
||||||
<div |
<div class="flex pl-6 pr-4 w-full overflow-x-hidden group py-4 hover:bg-gray-50 gap-3 relative cursor-pointer"> |
||||||
class="flex items-center gap-1 cursor-pointer nc-notification-item-wrapper" |
<div class="w-9.625"> |
||||||
:class="{ |
|
||||||
active: !item.is_read, |
|
||||||
}" |
|
||||||
> |
|
||||||
<div class="nc-notification-dot" :class="{ active: !item.is_read }"></div> |
|
||||||
<div class="nc-avatar-wrapper"> |
|
||||||
<slot name="avatar"> |
<slot name="avatar"> |
||||||
<div class="nc-notification-avatar"></div> |
<img src="~assets/img/brand/nocodb-logo.svg" alt="NocoDB" class="w-8" /> |
||||||
</slot> |
</slot> |
||||||
</div> |
</div> |
||||||
<div class="flex-grow ml-3"> |
|
||||||
<div class="flex items-center"> |
<div class="text-[13px] min-h-12 w-full leading-5"> |
||||||
<slot /> |
<slot /> |
||||||
</div> |
</div> |
||||||
<div |
<div v-if="item" class="text-xs whitespace-nowrap absolute right-4.1 bottom-5 text-gray-600"> |
||||||
v-if="item" |
{{ timeAgo(item.created_at) }} |
||||||
class="text-xs text-gray-500 mt-1" |
</div> |
||||||
|
<div class="flex items-start"> |
||||||
|
<NcTooltip v-if="!item.is_read"> |
||||||
|
<template #title> |
||||||
|
<span>Mark as read</span> |
||||||
|
</template> |
||||||
|
|
||||||
|
<NcButton |
||||||
|
:class="{ |
||||||
|
'!opacity-100': isMobileMode, |
||||||
|
}" |
||||||
|
type="secondary" |
||||||
|
class="!border-0 transition-all duration-100 opacity-0 !group-hover:opacity-100" |
||||||
|
size="xsmall" |
||||||
|
@click.stop="() => toggleRead(item)" |
||||||
|
> |
||||||
|
<GeneralIcon icon="check" class="text-gray-700" /> |
||||||
|
</NcButton> |
||||||
|
</NcTooltip> |
||||||
|
<NcDropdown |
||||||
|
v-else |
||||||
:class="{ |
:class="{ |
||||||
'text-primary': !item.is_read, |
'!opacity-100': isMobileMode, |
||||||
}" |
}" |
||||||
|
class="transition-all duration-100 opacity-0 !group-hover:opacity-100" |
||||||
> |
> |
||||||
{{ timeAgo(item.created_at) }} |
<NcButton size="xsmall" type="secondary" @click.stop> |
||||||
</div> |
<GeneralIcon icon="threeDotVertical" /> |
||||||
</div> |
</NcButton> |
||||||
<div @click.stop> |
|
||||||
<a-dropdown> |
|
||||||
<GeneralIcon v-if="!item.is_read" icon="threeDotVertical" class="nc-notification-menu-icon" /> |
|
||||||
<template #overlay> |
<template #overlay> |
||||||
<a-menu> |
<NcMenu> |
||||||
<a-menu-item @click="markAsRead(item)"> |
<NcMenuItem @click.stop="() => toggleRead(item)"> Mark as unread </NcMenuItem> |
||||||
<div class="p-2 text-xs">Mark as read</div> |
<NcDivider /> |
||||||
</a-menu-item> |
<NcMenuItem class="!text-red-500 !hover:bg-red-50" @click.stop="deleteNotification(item)"> Delete </NcMenuItem> |
||||||
</a-menu> |
</NcMenu> |
||||||
</template> |
</template> |
||||||
</a-dropdown> |
</NcDropdown> |
||||||
</div> |
</div> |
||||||
</div> |
</div> |
||||||
</template> |
</template> |
||||||
|
|
||||||
<style scoped lang="scss"> |
<style scoped lang="scss"></style> |
||||||
.nc-avatar-wrapper { |
|
||||||
@apply min-w-6 h-6 flex items-center justify-center; |
|
||||||
} |
|
||||||
|
|
||||||
.nc-notification-avatar { |
|
||||||
@apply w-6 h-6 rounded-full text-white font-weight-bold uppercase bg-gray-100; |
|
||||||
font-size: 0.7rem; |
|
||||||
} |
|
||||||
|
|
||||||
.nc-notification-dot { |
|
||||||
@apply min-w-2 min-h-2 mr-1 rounded-full; |
|
||||||
|
|
||||||
&.active { |
|
||||||
@apply bg-accent bg-opacity-100; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
.nc-notification-item-wrapper { |
|
||||||
.nc-notification-menu-icon { |
|
||||||
@apply !text-12px text-gray-500 opacity-0 transition-opacity duration-200 cursor-pointer; |
|
||||||
} |
|
||||||
|
|
||||||
&:hover { |
|
||||||
.nc-notification-menu-icon { |
|
||||||
@apply opacity-100; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
&.active { |
|
||||||
@apply bg-primary bg-opacity-4; |
|
||||||
} |
|
||||||
|
|
||||||
@apply py-3 px-3; |
|
||||||
} |
|
||||||
</style> |
|
||||||
|