Browse Source

Feature: Desktop-View - Migrate from another system.

Co-authored-by: Florian Liebe <fl@zammad.com>
Co-authored-by: Tobias Schäfer <ts@zammad.com>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Dominik Klein <dk@zammad.com>
Florian Liebe 1 year ago
parent
commit
7e1908320e

+ 49 - 0
app/frontend/apps/desktop/components/CommonButtonGroup/CommonButtonGroup.vue

@@ -0,0 +1,49 @@
+<!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
+
+<script setup lang="ts">
+import { computed } from 'vue'
+
+import CommonButton from '#desktop/components/CommonButton/CommonButton.vue'
+
+import type { CommonButtonItem } from './types.ts'
+
+export interface Props {
+  items: CommonButtonItem[]
+}
+
+const props = defineProps<Props>()
+
+const filteredItems = computed(() => {
+  return props.items.filter((item) => !item.hidden)
+})
+
+const onButtonClick = (item: CommonButtonItem) => {
+  if (item.disabled) return
+  item.onActionClick?.()
+}
+</script>
+
+<template>
+  <div class="flex flex-wrap gap-2">
+    <CommonButton
+      v-for="item of filteredItems"
+      :key="item.label"
+      :variant="item.variant"
+      :type="item.type"
+      :size="item.size"
+      :disabled="item.disabled"
+      :prefix-icon="item.icon"
+      :class="[
+        {
+          'opacity-50': item.disabled,
+        },
+      ]"
+      class="basis-full"
+      @click="onButtonClick(item)"
+    >
+      <slot name="item" v-bind="item">
+        {{ $t(item.label, ...(item.labelPlaceholder || [])) }}
+      </slot>
+    </CommonButton>
+  </div>
+</template>

+ 63 - 0
app/frontend/apps/desktop/components/CommonButtonGroup/__tests__/CommonButtonGroup.spec.ts

@@ -0,0 +1,63 @@
+// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
+
+import { renderComponent } from '#tests/support/components/index.ts'
+import { getByIconName } from '#tests/support/components/iconQueries.ts'
+
+import CommonButtonGroup from '../CommonButtonGroup.vue'
+import type { CommonButtonItem } from '../types.ts'
+
+const onActionClick = vi.fn()
+
+const items: CommonButtonItem[] = [
+  {
+    label: 'Button 1',
+    variant: 'primary',
+    icon: 'logo-flat',
+    onActionClick,
+  },
+  {
+    label: 'Button 2',
+    variant: 'secondary',
+  },
+  {
+    label: 'Button 3',
+    variant: 'tertiary',
+  },
+  {
+    label: 'Button 4',
+    variant: 'submit',
+    type: 'submit',
+  },
+  {
+    label: 'Button 5',
+    variant: 'danger',
+    size: 'large',
+  },
+]
+
+describe('CommonButtonGroup.vue', () => {
+  it('renders buttons with correct classes, types, ...', async () => {
+    const view = renderComponent(CommonButtonGroup, {
+      props: {
+        items,
+      },
+    })
+
+    const buttons = view.getAllByRole('button')
+    expect(buttons).toHaveLength(5)
+
+    const primaryButton = buttons[0]
+    expect(primaryButton).toHaveAttribute('type', 'button')
+    expect(primaryButton).toHaveClasses(['btn-primary', 'bg-blue-800'])
+    expect(getByIconName(primaryButton, 'logo-flat')).toBeInTheDocument()
+
+    await view.events.click(primaryButton)
+    expect(onActionClick).toHaveBeenCalledOnce()
+
+    const submitButton = buttons[3]
+    expect(submitButton).toHaveAttribute('type', 'submit')
+
+    const dangerButton = buttons[4]
+    expect(dangerButton).toHaveClasses(['btn-error', 'text-red-500'])
+  })
+})

+ 19 - 0
app/frontend/apps/desktop/components/CommonButtonGroup/types.ts

@@ -0,0 +1,19 @@
+// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
+
+import type {
+  ButtonSize,
+  ButtonType,
+  ButtonVariant,
+} from '#desktop/components/CommonButton/types.ts'
+
+export interface CommonButtonItem {
+  variant?: ButtonVariant
+  type?: ButtonType
+  size?: ButtonSize
+  label: string
+  labelPlaceholder?: string[]
+  disabled?: boolean
+  hidden?: boolean
+  icon?: string
+  onActionClick?: () => void
+}

+ 72 - 0
app/frontend/apps/desktop/components/CommonProgressBar/CommonProgressBar.vue

@@ -0,0 +1,72 @@
+<!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
+
+<script setup lang="ts">
+export interface Props {
+  value?: string
+  max?: string
+}
+
+const props = defineProps<Props>()
+</script>
+
+<template>
+  <progress class="progress" :value="props.value" :max="props.max"></progress>
+</template>
+
+<style scoped>
+.progress {
+  @apply rounded-box bg-blue-200 dark:bg-gray-700 h-2;
+
+  &::-moz-progress-bar {
+    @apply bg-blue-800 rounded-none;
+  }
+
+  &::-webkit-progress-bar {
+    @apply rounded-box;
+  }
+
+  &::-webkit-progress-value {
+    @apply bg-blue-800 rounded-none;
+
+    transition: width 1s;
+  }
+
+  &:indeterminate {
+    --progress-color: theme(colors.blue.800);
+  }
+
+  &:indeterminate {
+    background-image: repeating-linear-gradient(
+      90deg,
+      var(--progress-color) -1%,
+      var(--progress-color) 10%,
+      transparent 10%,
+      transparent 90%
+    );
+    background-size: 200%;
+    background-position-x: 15%;
+    animation: progress-loading 5s ease-in-out infinite;
+  }
+
+  &:indeterminate::-moz-progress-bar {
+    @apply bg-blue-200 dark:bg-gray-700;
+
+    background-image: repeating-linear-gradient(
+      90deg,
+      var(--progress-color) -1%,
+      var(--progress-color) 10%,
+      transparent 10%,
+      transparent 90%
+    );
+    background-size: 200%;
+    background-position-x: 15%;
+    animation: progress-loading 5s ease-in-out infinite;
+  }
+
+  @keyframes progress-loading {
+    50% {
+      background-position-x: -115%;
+    }
+  }
+}
+</style>

+ 29 - 0
app/frontend/apps/desktop/components/CommonProgressBar/__tests__/CommonProgressBar.spec.ts

@@ -0,0 +1,29 @@
+// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
+
+import { renderComponent } from '#tests/support/components/index.ts'
+import CommonProgressBar from '../CommonProgressBar.vue'
+
+describe('CommonProgressBar.vue', () => {
+  it('renders a progress bar with indeterminate state', async () => {
+    const view = renderComponent(CommonProgressBar)
+
+    const progressBar = view.getByRole('progressbar')
+    expect(progressBar).toHaveClass('progress')
+    expect(progressBar).not.toHaveAttribute('value')
+    expect(progressBar).not.toHaveAttribute('max')
+  })
+
+  it('renders a progress bar with given value + max', async () => {
+    const view = renderComponent(CommonProgressBar, {
+      props: {
+        value: '50',
+        max: '100',
+      },
+    })
+
+    const progressBar = view.getByRole('progressbar')
+    expect(progressBar).toHaveClass('progress')
+    expect(progressBar).toHaveAttribute('value', '50')
+    expect(progressBar).toHaveAttribute('max', '100')
+  })
+})

+ 1 - 1
app/frontend/apps/desktop/components/CommonSelect/CommonSelect.vue

@@ -324,7 +324,7 @@ const duration = VITE_TEST_MODE ? undefined : { enter: 300, leave: 200 }
               class="w-full px-2.5 py-1.5 flex justify-between gap-2"
             >
               <CommonLabel
-                class="ms-auto !text-blue-800 focus-visible:outline focus-visible:rounded-sm focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-blue-800"
+                class="ms-auto text-blue-800 dark:text-blue-800 focus-visible:outline focus-visible:rounded-sm focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-blue-800"
                 prefix-icon="check-all"
                 role="button"
                 tabindex="1"

+ 4 - 1
app/frontend/apps/desktop/components/Form/fields/FieldGroupPermissions/FieldGroupPermissionsInput.vue

@@ -250,7 +250,10 @@ const ensureGranularOrFullAccess = (
         "
       >
         <template #label>
-          <CommonLabel class="uppercase text-gray-300" size="small">
+          <CommonLabel
+            class="uppercase text-gray-300 dark:text-gray-300"
+            size="small"
+          >
             {{ $t(groupAccess.label) }}
           </CommonLabel>
         </template>

+ 4 - 1
app/frontend/apps/desktop/components/Form/fields/FieldImageUpload/FieldImageUploadInput.vue

@@ -97,7 +97,10 @@ const { isOverDropZone } = useDropZone(dropZoneRef, {
       v-if="isOverDropZone"
       class="w-full rounded outline-dashed outline-1 outline-blue-800 text-center"
     >
-      <CommonLabel class="py-2 !text-blue-800" prefix-icon="upload">
+      <CommonLabel
+        class="py-2 text-blue-800 dark:text-blue-800"
+        prefix-icon="upload"
+      >
         {{ $t('Drop image file here') }}
       </CommonLabel>
     </div>

+ 2 - 2
app/frontend/apps/desktop/components/Form/fields/FieldTreeSelect/FieldTreeSelectInputDropdown.vue

@@ -433,7 +433,7 @@ const duration = VITE_TEST_MODE ? undefined : { enter: 300, leave: 200 }
             >
               <CommonLabel
                 v-if="currentPath.length"
-                class="!text-blue-800 focus-visible:outline focus-visible:rounded-sm focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-blue-800"
+                class="text-blue-800 dark:text-blue-800 focus-visible:outline focus-visible:rounded-sm focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-blue-800"
                 :prefix-icon="
                   locale.localeData?.dir === 'rtl'
                     ? 'chevron-right'
@@ -450,7 +450,7 @@ const duration = VITE_TEST_MODE ? undefined : { enter: 300, leave: 200 }
               </CommonLabel>
               <CommonLabel
                 v-if="multiple && hasMoreSelectableOptions"
-                class="ms-auto !text-blue-800 focus-visible:outline focus-visible:rounded-sm focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-blue-800"
+                class="ms-auto text-blue-800 dark:text-blue-800 focus-visible:outline focus-visible:rounded-sm focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-blue-800"
                 prefix-icon="check-all"
                 role="button"
                 tabindex="2"

+ 10 - 0
app/frontend/apps/desktop/initializer/3RD-PARTY-ICONS.md

@@ -10,6 +10,7 @@
 - `assets/chevron-left.svg`
 - `assets/chevron-right.svg`
 - `assets/dash-circle.svg`
+- `assets/download.svg`
 - `assets/exclamation-triangle.svg`
 - `assets/eye-slash.svg`
 - `assets/eye.svg`
@@ -27,40 +28,49 @@
 - `assets/upload.svg`
 - `assets/x-circle.svg`
 - `assets/x-lg.svg`
+
   - Author: The Bootstrap Authors
   - License: MIT
   - URL: https://github.com/twbs/icons
 
 - `assets/github.svg`
+
   - Author: GitHub
   - URL: https://github.com/logos
 
 - `assets/gitlab.svg`
+
   - Author: GitLab
   - URL: https://about.gitlab.com/press/press-kit/
   - URL: https://github.com/logos
 
 - `assets/google.svg`
+
   - Author: Google
   - URL: https://about.google/brand-resource-center/
 
 - `assets/facebook.svg`
+
   - Author: Facebook
   - URL: https://www.facebook.com/brand/resources/facebookapp/logo
 
 - `assets/linkedin.svg`
+
   - Author: LinkedIn
   - URL: https://brand.linkedin.com/downloads
 
 - `assets/microsoft.svg`
+
   - Author: Microsoft
   - URL: https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks
 
 - `assets/saml.svg`
+
   - Author: OASIS
   - URL: https://saml.xml.org/wiki/saml-logos
 
 - `assets/sina-weibo.svg`
+
   - Author: Sina Weibo
   - URL: https://weibo.com
 

Some files were not shown because too many files changed in this diff