diff --git a/apps/web/src/components/UiIcon.vue b/apps/web/src/components/UiIcon.vue
index 03f62c6..3330e0f 100644
--- a/apps/web/src/components/UiIcon.vue
+++ b/apps/web/src/components/UiIcon.vue
@@ -68,6 +68,8 @@ defineProps<{
| 'pulse'
| 'sliders'
| 'camera'
+ | 'download'
+ | 'upload'
| 'arrow-right'
| 'arrow-left'
| 'chevron-right'
@@ -211,6 +213,16 @@ defineProps<{
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web/src/views/ActivitiesView.vue b/apps/web/src/views/ActivitiesView.vue
index 48c2506..c23636e 100644
--- a/apps/web/src/views/ActivitiesView.vue
+++ b/apps/web/src/views/ActivitiesView.vue
@@ -163,6 +163,12 @@ const board = computed(() => {
});
const currentBoard = computed(() => boards.value.find((b) => b.id === board.value) ?? null);
const boardMenuOpen = ref(false);
+const actionsMenuOpen = ref(false);
+const importBtn = ref(null);
+
+function triggerImport() {
+ importBtn.value?.click();
+}
const boardModal = ref<{ mode: 'create' | 'edit'; name: string; memberIds: string[] } | null>(null);
const boardMemberQuery = ref('');
const boardSaving = ref(false);
@@ -965,9 +971,29 @@ function migrateLegacyDoneFilter() {
return true;
}
+const timeChipsEl = ref(null);
+
+function revealActiveTimeChip() {
+ if (!isMobile.value) return;
+ const root = timeChipsEl.value;
+ const active = root?.querySelector('button.on');
+ if (!root || !active) return;
+ const rootRect = root.getBoundingClientRect();
+ const activeRect = active.getBoundingClientRect();
+ const fullyVisible =
+ activeRect.left >= rootRect.left - 1 && activeRect.right <= rootRect.right + 1;
+ if (fullyVisible) return;
+ const delta =
+ activeRect.left < rootRect.left
+ ? activeRect.left - rootRect.left
+ : activeRect.right - rootRect.right;
+ root.scrollBy({ left: delta });
+}
+
let mobileQuery: MediaQueryList | null = null;
function syncMobile() {
isMobile.value = mobileQuery?.matches ?? false;
+ void nextTick(revealActiveTimeChip);
}
onMounted(() => {
@@ -992,6 +1018,14 @@ watch(viewMode, () => {
menuId.value = null;
});
+watch(filter, () => {
+ void nextTick(revealActiveTimeChip);
+});
+
+watch(counts, () => {
+ void nextTick(revealActiveTimeChip);
+});
+
onUnmounted(() => {
mobileQuery?.removeEventListener('change', syncMobile);
unbindBoardMenuDismiss();
@@ -1016,29 +1050,44 @@ onUnmounted(() => {
+
- انواع فعالیت و فیلدها
+ انواع فعالیت و فیلدها
- مدیریت ستونها
+ مدیریت ستونها
-
+
+
خروجی اکسل
- خروجی
{
@settled="onImportSettled"
>
-
+
+
ورود از اکسل
- ورود
@@ -1105,10 +1163,100 @@ onUnmounted(() => {
+
+
+ (actionsMenuOpen = v)"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
-