Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
69c8fba
feat: add nuxt scripts
itsmohmans Jan 31, 2026
a4575e3
feat: add matomo script and env variables
itsmohmans Jan 31, 2026
530ecb0
fix: treat the whole text input as a single comment
itsmohmans Jan 15, 2026
6a7b332
fix: strictly check for column header name in csv/excel parsing
itsmohmans Jan 15, 2026
f19b81c
refactor: use csv parser for both xlsx and csv
itsmohmans Jan 15, 2026
d0685c1
fix: upload file templates date formatting
itsmohmans Jan 19, 2026
9b07258
refactor: unify the name of 'comment' property name
itsmohmans Jan 19, 2026
adfe6b6
fix: use correct encoding for downloaded files
itsmohmans Jan 19, 2026
397d84f
fix: hide platform column condition
itsmohmans Jan 19, 2026
34c4a8d
feat: enhance mobile responsiveness of data table
itsmohmans Jan 19, 2026
ac9cfe0
fix: table pagination on mobile styline
itsmohmans Jan 21, 2026
649f6d9
fix: update paginator visibility and rows per page options based on t…
itsmohmans Jan 21, 2026
374313b
fix: analysis results table styling
itsmohmans Jan 22, 2026
f1be4d0
feat: update file download templates UX
itsmohmans Jan 22, 2026
adf15a8
feat: add XLSX download functionality
itsmohmans Jan 22, 2026
024e463
fix: enhance print functionality in analysis results
itsmohmans Jan 22, 2026
95fb87d
fix: update xlsx download button style
itsmohmans Jan 28, 2026
c0d1d83
feat: implement auto-detect file drag functionality in analysis form
itsmohmans Jan 28, 2026
4353432
ci: migrate pre-commit hooks registry to josa private registry (#187)
thamudi Feb 2, 2026
574e4a8
chore: recreate yarn.lock
thread-koder Feb 2, 2026
4c94610
chore: add uuid package
itsmohmans Feb 20, 2026
85c94a6
refactor: move redis initialization to a separate shared util
itsmohmans Feb 20, 2026
77a3a20
feat: add ai analysis queuing
itsmohmans Feb 20, 2026
67400da
feat: add endpoint to get analysis overview
itsmohmans Feb 21, 2026
4653389
feat(analysis-overview): add types
itsmohmans Feb 21, 2026
c65fffc
fix(analyze): add locale to the api analysis job meta and request
itsmohmans Feb 21, 2026
5f45f4d
feat: add analyze and results endpoints
itsmohmans Feb 21, 2026
9a8e2e2
feat: implement partial analysis and progress in results page
itsmohmans Feb 27, 2026
215b695
refactor: update analysis response structure in analyze form
itsmohmans Feb 27, 2026
1fc7ddf
refactor: update analysis download handling
itsmohmans Feb 28, 2026
ebae012
style: eslint formatting
itsmohmans Feb 28, 2026
118e3ce
feat: add single comment view
itsmohmans Feb 28, 2026
b8c54c4
feat: add loading overlay on top of charts container
itsmohmans Feb 28, 2026
240afa0
feat: add progress bar for analysis processing
itsmohmans Feb 28, 2026
fd9eed2
refactor: update Redis key prefix from 'nuha_auth' to 'nuha'
itsmohmans Feb 28, 2026
0209a9a
feat: fetch all comments before printing
itsmohmans Feb 28, 2026
6f7d2fe
style: enhance print styles
itsmohmans Feb 28, 2026
e24af8c
chore: i18n
itsmohmans Feb 28, 2026
827813d
feat: add share functionality to analysis results
itsmohmans Feb 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ NUXT_REDIS_HOST=localhost
NUXT_REDIS_PORT=6379
NUXT_REDIS_PASSWORD=
NUXT_REDIS_DB=0
NUXT_REDIS_KEY_PREFIX=nuha_auth:
NUXT_REDIS_KEY_PREFIX=nuha:

# Strapi configs
STRAPI_URL=
Expand Down
11 changes: 9 additions & 2 deletions .sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ NUXT_REDIS_PORT=6379
NUXT_REDIS_PASSWORD=supersecret
#samplr#NUXT_REDIS_DB=0
NUXT_REDIS_DB=0
#samplr#NUXT_REDIS_KEY_PREFIX=nuha_auth:
NUXT_REDIS_KEY_PREFIX=nuha_auth:
#samplr#NUXT_REDIS_KEY_PREFIX=nuha:
NUXT_REDIS_KEY_PREFIX=nuha:

# Base URL (update for production)
#samplr#NUXT_PUBLIC_BASE_URL=http://localhost:3000
Expand All @@ -75,3 +75,10 @@ NUXT_API_PARTY_ENDPOINTS_CMS_URL=http://localhost:1337
NUXT_API_PARTY_ENDPOINTS_CMS_TOKEN=
#samplr#NUXT_PUBLIC_CMS_PREFIX=/api
NUXT_PUBLIC_CMS_PREFIX=/api

#samplr#NUXT_PUBLIC_SCRIPTS_MATOMO_ANALYTICS_MATOMO_URL=
NUXT_PUBLIC_SCRIPTS_MATOMO_ANALYTICS_MATOMO_URL=
#samplr#NUXT_PUBLIC_SCRIPTS_MATOMO_ANALYTICS_SITE_ID=
NUXT_PUBLIC_SCRIPTS_MATOMO_ANALYTICS_SITE_ID=
#samplr#NUXT_PUBLIC_SCRIPTS_MATOMO_ANALYTICS_TRACKER_URL=
NUXT_PUBLIC_SCRIPTS_MATOMO_ANALYTICS_TRACKER_URL=
2 changes: 1 addition & 1 deletion .woodpecker/build-latest-image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ variables:

steps:
- name: run-pre-commit-hooks
image: josaorg/pre-commit-runner
image: registry.cloud.josa.ngo/library/pre-commit-runner
settings:
args: "--all-files"
skip: "end-of-file-fixer, run-samplr"
Expand Down
2 changes: 1 addition & 1 deletion .woodpecker/build-stable-image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ variables:

steps:
- name: run-pre-commit-hooks
image: josaorg/pre-commit-runner
image: registry.cloud.josa.ngo/library/pre-commit-runner
settings:
args: "--all-files"
skip: "end-of-file-fixer, run-samplr"
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ ENV NUXT_REDIS_HOST=redis
ENV NUXT_REDIS_PORT=6379
ENV NUXT_REDIS_PASSWORD=
ENV NUXT_REDIS_DB=0
ENV NUXT_REDIS_KEY_PREFIX=nuha_auth:
ENV NUXT_REDIS_KEY_PREFIX=nuha:

COPY --from=builder --chown=nuxt:nodejs /app/.output /app/.output

Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ NUXT_AI_MODEL_URL=your-ai-endpoint
# Sets the default AI model dialect
# Must match a supported region code from your the CMS (e.g., 'egy' for Egyptian model)
NUXT_PUBLIC_AI_MODEL_DEFAULT_REGION=egy

# Matomo Analytics Configurations
NUXT_PUBLIC_SCRIPTS_MATOMO_ANALYTICS_MATOMO_URL=
NUXT_PUBLIC_SCRIPTS_MATOMO_ANALYTICS_SITE_ID=
NUXT_PUBLIC_SCRIPTS_MATOMO_ANALYTICS_TRACKER_URL=
```

## CMS Setup
Expand Down
10 changes: 5 additions & 5 deletions assets/css/typography.css
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
@layer base {
h1 {
@apply font-LTZarid text-h1-m lg:text-h1;
@apply font-LTZarid text-h1-m lg:text-h1 print:!font-LTZarid;
}

h2 {
@apply font-LTZarid text-h2-m lg:text-h2;
@apply font-LTZarid text-h2-m lg:text-h2 print:!font-LTZarid;
}

h3 {
@apply font-LTZarid text-h3-m lg:text-h3;
@apply font-LTZarid text-h3-m lg:text-h3 print:!font-LTZarid;
}

h4 {
@apply font-LTZarid text-h4-m lg:text-h4;
@apply font-LTZarid text-h4-m lg:text-h4 print:!font-LTZarid;
}

body {
@apply font-IBMPlexSansArabic text-base font-normal;
@apply font-IBMPlexSansArabic text-base font-normal print:!font-IBMPlexSansArabic;
}

small {
Expand Down
104 changes: 89 additions & 15 deletions components/Analyze/DownloadModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,31 @@
/>

<div class="grid grid-cols-1 gap-3">
<!-- XLSX Download (Highlighted) -->
<UiButton
variant="outline"
size="lg"
class="h-auto p-4"
:disabled="downloading"
@click="downloadXLSX"
>
<template #icon>
<Icon name="mdi:file-excel" size="24" />
</template>
<div class="flex-1 text-start">
<div class="font-bold">
{{ $t('analyze.results.download.xlsx') }}
</div>
<div class="mt-1 text-sm opacity-70">
{{ $t('analyze.results.download.xlsxDescription') }}
</div>
<div class="mt-1 flex items-center gap-1 text-sm opacity-90">
<Icon name="mdi:star" size="18" />
{{ $t('analyze.help.fileUpload.excelHint') }}
</div>
</div>
</UiButton>

<!-- PDF Download -->
<UiButton
variant="ghost"
Expand All @@ -41,44 +66,44 @@
</div>
</UiButton>

<!-- CSV Download -->
<!-- JSON Download -->
<UiButton
variant="ghost"
size="lg"
class="h-auto border border-colors-neutral-foreground border-opacity-20 p-4"
:disabled="downloading"
@click="downloadCSV"
@click="downloadJSON"
>
<template #icon>
<Icon name="mdi:file-table" size="24" class="text-green-600" />
<Icon name="mdi:code-json" size="24" class="text-blue-600" />
</template>
<div class="flex-1 text-start">
<div class="font-medium">
{{ $t('analyze.results.download.csv') }}
{{ $t('analyze.results.download.json') }}
</div>
<div class="mt-1 text-sm opacity-70">
{{ $t('analyze.results.download.csvDescription') }}
{{ $t('analyze.results.download.jsonDescription') }}
</div>
</div>
</UiButton>

<!-- JSON Download -->
<!-- CSV Download -->
<UiButton
variant="ghost"
size="lg"
class="h-auto border border-colors-neutral-foreground border-opacity-20 p-4"
:disabled="downloading"
@click="downloadJSON"
@click="downloadCSV"
>
<template #icon>
<Icon name="mdi:code-json" size="24" class="text-blue-600" />
<Icon name="mdi:file-table" size="24" class="text-green-600" />
</template>
<div class="flex-1 text-start">
<div class="font-medium">
{{ $t('analyze.results.download.json') }}
{{ $t('analyze.results.download.csv') }}
</div>
<div class="mt-1 text-sm opacity-70">
{{ $t('analyze.results.download.jsonDescription') }}
{{ $t('analyze.results.download.csvDescription') }}
</div>
</div>
</UiButton>
Expand All @@ -101,7 +126,8 @@

interface Props {
modelValue: boolean
analysisData: AIAnalysisResponse | null
analysisData?: AIAnalysisResponse | null
jobId?: string
onPrintPDF?: () => void
}

Expand Down Expand Up @@ -136,7 +162,7 @@
}

const downloadFile = async (endpoint: string, filename: string) => {
if (!props.analysisData) {
if (!props.jobId && !props.analysisData) {
showNotification(t('analyze.results.download.downloadError'), 'error')
return
}
Expand All @@ -147,7 +173,8 @@
const response = await $fetch(endpoint, {
method: 'POST',
body: {
data: props.analysisData,
job_id: props.jobId,
// data: props.analysisData,
},
})

Expand All @@ -157,7 +184,7 @@
if (endpoint.includes('csv')) {
// CSV response is already a string
fileContent = response as string
mimeType = 'text/csv'
mimeType = 'text/csv;charset=utf-8'
} else {
// JSON response needs to be stringified if it's an object
fileContent =
Expand All @@ -168,7 +195,9 @@
}

// Create blob and download
const blob = new Blob([fileContent], { type: mimeType })
const blob = new Blob([new Uint8Array([0xef, 0xbb, 0xbf]), fileContent], {
type: mimeType,
})

const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
Expand Down Expand Up @@ -200,6 +229,51 @@
}
}

const downloadXLSX = async () => {
if (!props.jobId && !props.analysisData) {
showNotification(t('analyze.results.download.downloadError'), 'error')
return
}

downloading.value = true

try {
const response = await $fetch('/api/analyze/download/xlsx', {
method: 'POST',
body: {
job_id: props.jobId,
// data: props.analysisData,
},
responseType: 'arrayBuffer',
})

const timestamp = new Date()
.toISOString()
.replace(/[:.]/g, '-')
.slice(0, -5)

const blob = new Blob([response], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
})

const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `nuha_analysis_results_${timestamp}.xlsx`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)

showNotification(t('analyze.results.download.downloadSuccess'))
} catch (error) {
console.error('Download error:', error)
showNotification(t('analyze.results.download.downloadError'), 'error')
} finally {
downloading.value = false
}
}

const downloadCSV = () => {
// ISO 8601 format: YYYY-MM-DDTHH-MM-SS
const timestamp = new Date()
Expand Down
Loading