diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 000000000..6673a8093
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,65 @@
+name: Build Container Image
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - main
+ tags:
+ - "*"
+
+jobs:
+ build:
+ name: Build
+
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ arch: [amd64, arm64]
+
+ steps:
+ - name: Checkout Entire Repository
+ uses: actions/checkout@v4
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+ with:
+ platforms: linux/${{ matrix.arch }}
+
+ - name: Login to GitHub Container Registry
+ uses: docker/login-action@v2
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Set Branch
+ run: |
+ export APPS_JSON='[{"url": "https://github.com/frappe/helpdesk","branch": "main"}]'
+ echo "APPS_JSON_BASE64=$(echo $APPS_JSON | base64 -w 0)" >> $GITHUB_ENV
+ echo "FRAPPE_BRANCH=version-15" >> $GITHUB_ENV
+
+ - name: Set Image Tag
+ run: |
+ echo "IMAGE_TAG=stable" >> $GITHUB_ENV
+
+ - uses: actions/checkout@v4
+ with:
+ repository: frappe/frappe_docker
+ path: builds
+
+ - name: Build and push
+ uses: docker/build-push-action@v6
+ with:
+ push: true
+ context: builds
+ file: builds/images/layered/Containerfile
+ tags: >
+ ghcr.io/${{ github.repository }}:${{ github.ref_name }},
+ ghcr.io/${{ github.repository }}:${{ env.IMAGE_TAG }}
+ build-args: |
+ "FRAPPE_BRANCH=${{ env.FRAPPE_BRANCH }}"
+ "APPS_JSON_BASE64=${{ env.APPS_JSON_BASE64 }}"
\ No newline at end of file
diff --git a/desk/src/App.vue b/desk/src/App.vue
index bebc1e316..2431393f0 100644
--- a/desk/src/App.vue
+++ b/desk/src/App.vue
@@ -15,7 +15,7 @@ import { stopSession } from "@/telemetry";
import { Dialogs } from "frappe-ui";
useConfigStore();
-onMounted(async () => {
+onMounted(() => {
window.addEventListener("online", () => {
createToast({
title: "You are now online",
diff --git a/desk/src/components/EmailEditor.vue b/desk/src/components/EmailEditor.vue
index db502a7bc..184729346 100644
--- a/desk/src/components/EmailEditor.vue
+++ b/desk/src/components/EmailEditor.vue
@@ -157,7 +157,7 @@ import {
TextEditorFixedMenu,
createResource,
} from "frappe-ui";
-import { validateEmail } from "@/utils";
+import { createToast, validateEmail } from "@/utils";
import {
MultiSelectInput,
AttachmentItem,
@@ -165,6 +165,7 @@ import {
} from "@/components";
import { AttachmentIcon, EmailIcon } from "@/components/icons";
import { PreserveVideoControls } from "@/tiptap-extensions";
+import { useError } from "@/composables/error";
const editorRef = ref(null);
const showCannedResponseSelectorModal = ref(false);
@@ -242,6 +243,15 @@ function submitMail() {
emit("submit");
loading.value = false;
},
+ onError: (err) => {
+ loading.value = false;
+ createToast({
+ title: err.exc_type,
+ text: err.messages[0],
+ icon: "x",
+ iconClasses: "text-red-500",
+ });
+ },
});
sendMail.submit();
diff --git a/desk/src/components/ListViewBuilder.vue b/desk/src/components/ListViewBuilder.vue
index a08c9a581..427ed2350 100644
--- a/desk/src/components/ListViewBuilder.vue
+++ b/desk/src/components/ListViewBuilder.vue
@@ -7,7 +7,7 @@
>
;
};
}
@@ -134,7 +141,15 @@ interface E {
(event: "rowClick", row: any): void;
}
-const props = defineProps
();
+const props = withDefaults(defineProps
(), {
+ options: () => {
+ return {
+ doctype: "",
+ hideViewControls: false,
+ selectable: true,
+ };
+ },
+});
const emit = defineEmits();
const { isMobileView } = useScreenSize();
@@ -190,10 +205,21 @@ function handleColumnConfig(column) {
return column;
}
+const statusMap: Record = props.options
+ .statusMap as Record;
+function handleStatusColor(status: "Published" | "Draft"): BadgeStatus {
+ if (!statusMap)
+ return {
+ label: status,
+ theme: "gray",
+ };
+ return statusMap[status];
+}
+
const filterableFields = createResource({
url: "helpdesk.api.doc.get_filterable_fields",
cache: ["DocField", props.options.doctype],
- auto: true,
+ auto: !props.options.hideViewControls,
params: {
doctype: props.options.doctype,
append_assign: true,
@@ -212,7 +238,7 @@ const filterableFields = createResource({
const sortableFields = createResource({
url: "helpdesk.api.doc.sort_options",
- auto: true,
+ auto: !props.options.hideViewControls,
params: {
doctype: props.options.doctype,
},
@@ -220,7 +246,7 @@ const sortableFields = createResource({
const quickFilters = createResource({
url: "helpdesk.api.doc.get_quick_filters",
- auto: true,
+ auto: !props.options.hideViewControls,
params: {
doctype: props.options.doctype,
},
@@ -232,7 +258,12 @@ const quickFilters = createResource({
});
const showViewControls = computed(() => {
- return filterableFields.data && sortableFields.data && quickFilters.data;
+ return (
+ !props.options.hideViewControls &&
+ filterableFields.data &&
+ sortableFields.data &&
+ quickFilters.data
+ );
});
const listViewData = reactive({
diff --git a/desk/src/components/desk/global/NewContactDialog.vue b/desk/src/components/desk/global/NewContactDialog.vue
index cc6619524..5cdf1ffed 100644
--- a/desk/src/components/desk/global/NewContactDialog.vue
+++ b/desk/src/components/desk/global/NewContactDialog.vue
@@ -51,6 +51,7 @@ import {
ErrorMessage,
createResource,
Autocomplete,
+ createListResource,
} from "frappe-ui";
import zod from "zod";
@@ -141,20 +142,17 @@ const open = computed({
},
});
-const customerResource = createResource({
- url: "helpdesk.extends.client.get_list",
- params: {
- doctype: "HD Customer",
- fields: ["name", "customer_name"],
- },
+const customerResource = createListResource({
+ doctype: "HD Customer",
+ fields: ["name"],
+ cache: "customers",
transform: (data) => {
- let allData = data.map((option) => {
+ return data.map((option) => {
return {
label: option.name,
- value: option.customer_name,
+ value: option.name,
};
});
- return allData;
},
auto: true,
});
diff --git a/desk/src/components/index.ts b/desk/src/components/index.ts
index edc4b21a5..339ab9153 100644
--- a/desk/src/components/index.ts
+++ b/desk/src/components/index.ts
@@ -1,7 +1,6 @@
export { default as AttachmentItem } from "./AttachmentItem.vue";
export { default as CommandPalette } from "./command-palette/CP.vue";
export { default as HCard } from "./HCard.vue";
-export { default as ListView } from "./list-view/LV.vue";
export { default as NestedPopover } from "./NestedPopover.vue";
export { default as Notifications } from "./notifications/Notifications.vue";
export { default as PageTitle } from "./PageTitle.vue";
diff --git a/desk/src/components/list-view/LV.vue b/desk/src/components/list-view/LV.vue
deleted file mode 100644
index fba5e59b2..000000000
--- a/desk/src/components/list-view/LV.vue
+++ /dev/null
@@ -1,108 +0,0 @@
-
-
-
-
-
-
handleScroll()"
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/desk/src/components/list-view/LVEmpty.vue b/desk/src/components/list-view/LVEmpty.vue
deleted file mode 100644
index 82ec350ed..000000000
--- a/desk/src/components/list-view/LVEmpty.vue
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
diff --git a/desk/src/components/list-view/LVHeader.vue b/desk/src/components/list-view/LVHeader.vue
deleted file mode 100644
index 00ad57645..000000000
--- a/desk/src/components/list-view/LVHeader.vue
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
- {{ c.label }}
-
-
-
-
-
-
-
-
diff --git a/desk/src/components/list-view/LVLoading.vue b/desk/src/components/list-view/LVLoading.vue
deleted file mode 100644
index eb8c23fea..000000000
--- a/desk/src/components/list-view/LVLoading.vue
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
diff --git a/desk/src/components/list-view/LVNavigation.vue b/desk/src/components/list-view/LVNavigation.vue
deleted file mode 100644
index 9ef6ed398..000000000
--- a/desk/src/components/list-view/LVNavigation.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
- {
- resource.update({
- pageLength: val,
- });
- resource.reload();
- }
- "
- />
-
-
-
- {{ resource.data?.length }}
- of
- {{ resource.totalCount }}
-
-
-
-
-
-
diff --git a/desk/src/components/list-view/LVRow.vue b/desk/src/components/list-view/LVRow.vue
deleted file mode 100644
index d0217c44c..000000000
--- a/desk/src/components/list-view/LVRow.vue
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
-
-
{
- if (isFunction(data.onClick)) {
- event.preventDefault();
- data.onClick();
- }
- }
- "
- >
-
-
-
filterFunc(event, c)"
- >
-
- {{ data[c.key] || "⸺" }}
-
-
-
-
-
-
-
-
-
diff --git a/desk/src/components/list-view/LVSelectionBar.vue b/desk/src/components/list-view/LVSelectionBar.vue
deleted file mode 100644
index 6145c14c0..000000000
--- a/desk/src/components/list-view/LVSelectionBar.vue
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
- {{ selection.storage.size }}
- {{ selection.storage.size < 2 ? singular : plural }} selected
-
-
-
-
-
|
-
-
-
-
-
-
diff --git a/desk/src/components/list-view/selection.ts b/desk/src/components/list-view/selection.ts
deleted file mode 100644
index fc77c7e5c..000000000
--- a/desk/src/components/list-view/selection.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { reactive } from "vue";
-import { Action, Key } from "./types";
-
-export const selection = reactive({
- storage: new Set(),
- actions: new Set(),
- toggle: (key: Key) => {
- if (!selection.storage.delete(key)) {
- selection.storage.add(key);
- }
- },
- reset: () => {
- selection.storage.clear();
- selection.actions.clear();
- },
-});
diff --git a/desk/src/components/list-view/symbols.ts b/desk/src/components/list-view/symbols.ts
deleted file mode 100644
index b3f6d269e..000000000
--- a/desk/src/components/list-view/symbols.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { InjectionKey, Ref } from "vue";
-import { Resource, Column } from "@/types";
-
-type I = {
- name: string;
- [key: string]: unknown;
-};
-type R = Resource>;
-export const CheckboxKey: InjectionKey = Symbol("Checkbox");
-export const ColumnsKey: InjectionKey> = Symbol("Columns");
-export const DocTypeKey: InjectionKey = Symbol("DocType");
-export const FilterKey: InjectionKey = Symbol("Filter");
-export const IdKey: InjectionKey = Symbol("Id");
-export const PluralKey: InjectionKey[> = Symbol("Plural");
-export const ResourceKey: InjectionKey = Symbol("Resource");
-export const SingluarKey: InjectionKey][> = Symbol("Singular");
diff --git a/desk/src/components/list-view/types.ts b/desk/src/components/list-view/types.ts
deleted file mode 100644
index 4f67ee37f..000000000
--- a/desk/src/components/list-view/types.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export type Selection = Set;
-export type Key = string;
-export type Action = {
- label: string;
- onClick: (key: Key[]) => void;
-};
diff --git a/desk/src/components/notifications/Notifications.vue b/desk/src/components/notifications/Notifications.vue
index 1b8fcf3b4..4356fdb33 100644
--- a/desk/src/components/notifications/Notifications.vue
+++ b/desk/src/components/notifications/Notifications.vue
@@ -48,13 +48,11 @@
}
"
>
-
+
]
- {{
- n.user_from.name
- }}
+ {{ n.user_from }}
mentioned you in ticket
diff --git a/desk/src/components/ticket/TicketsListView.vue b/desk/src/components/ticket/TicketsListView.vue
index e156d455e..bceacc902 100644
--- a/desk/src/components/ticket/TicketsListView.vue
+++ b/desk/src/components/ticket/TicketsListView.vue
@@ -72,7 +72,7 @@
- {{ item || "-" }}
+ {{ item }}
-