Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
a5543a1
feat: spilt client access key to share component
pingkunga May 3, 2026
c51e3b3
feat: version page get version from redmine project id that config in…
pingkunga May 3, 2026
cc1ae31
feat: fix ts warn + default project on create
pingkunga May 3, 2026
b43ab41
UI: Improve Style
pingkunga May 3, 2026
f28a495
UI: Add Menu and Add New Line After /n
pingkunga May 3, 2026
d1d7a3f
feat: add frontend ui for create build req
pingkunga May 3, 2026
d432e22
feat: add server side api fro create common build req
pingkunga May 4, 2026
5463257
do :update
pingkunga May 4, 2026
e865c93
feat: excel export (package file-saver / xlsx )
pingkunga May 4, 2026
2866f08
feat: export excel
pingkunga May 4, 2026
d39ac4d
feat: export with preserve newline
pingkunga May 4, 2026
e43148a
fixbug Nuxt Docker Build Error failed to solve: invalid file request …
pingkunga May 4, 2026
8430c2d
feat: by pass for same server
pingkunga May 11, 2026
6a828f0
feat: show this week version info
pingkunga May 11, 2026
4cd3c1e
fixbug: home permission error
pingkunga May 11, 2026
599604f
fixbug: reverse
pingkunga May 11, 2026
e2e9432
fixbug : by pass is same site
pingkunga May 11, 2026
d7243d9
fixbug: bypass if from frontend
pingkunga May 11, 2026
dbc03a1
fixbug : bypass same site
pingkunga May 11, 2026
54caa1a
fixbug: by pass samesite
pingkunga May 11, 2026
ffd409f
feat: use x-internal-key
pingkunga May 12, 2026
e0913e9
feat: add gitlab api + type + sample project config
pingkunga May 31, 2026
c2af0c0
feat: add helper
pingkunga May 31, 2026
51d9af5
feat: add nuxt config
pingkunga May 31, 2026
f6adb1f
feat: init ui
pingkunga May 31, 2026
4ae7368
feat: add menu
pingkunga May 31, 2026
326818a
add GitLabCacheData / note will remove @ts-ignore later
pingkunga Jun 3, 2026
9899ec6
feat: add feature sync gitlab event for store who created branch
pingkunga Jun 3, 2026
673d1ad
feat: pagination branch
pingkunga Jun 4, 2026
91121f4
feat: add filter
pingkunga Jun 4, 2026
9b54bc1
doc set current ver
pingkunga Jun 4, 2026
fdf1265
doc: update
pingkunga Jun 4, 2026
fa295e2
feat: Add Export Excel
pingkunga Jun 4, 2026
9132585
Initial plan
Copilot Jun 5, 2026
a521884
feat: add age days branch column
Copilot Jun 5, 2026
3caa91c
style: align branch age helper formatting
Copilot Jun 5, 2026
2299aaa
refactor: reuse computed branch age cell value
Copilot Jun 5, 2026
256579e
fix: harden branch age days calculation
Copilot Jun 5, 2026
a9c4a99
refactor: simplify branch age days cell
Copilot Jun 5, 2026
97c60e5
refactor: streamline age days cell rendering
Copilot Jun 5, 2026
decdd31
refactor: extract branch age day constant
Copilot Jun 5, 2026
456c198
style: align branch age constant naming
Copilot Jun 5, 2026
6a65d45
refactor: localize branch age day constant
Copilot Jun 5, 2026
a68916d
Apply remaining changes
Copilot Jun 5, 2026
b54dc5e
Merge pull request #6 from pingkunga/copilot/add-age-days-column
pingkunga Jun 5, 2026
30a9081
feat: create common helper for grouping
pingkunga Jun 7, 2026
9ebb10b
feat: remove @ts-ignore
pingkunga Jun 7, 2026
0b6d6ca
feat: Update for sort / group
pingkunga Jun 7, 2026
152bd4e
feat: fix ts-warning
pingkunga Jun 7, 2026
4cd2da2
doc: set new version
pingkunga Jun 7, 2026
2ae0c78
fixbug: Cannot find module '/app/node_modules/xlsx/dist/cpexcel.js' f…
pingkunga Jun 8, 2026
a1fa534
feat: default share type
pingkunga Jun 8, 2026
73e0f10
doc: update
pingkunga Jun 8, 2026
ccdbc9b
fixbug: Cannot find module '/app/node_modules/xlsx/dist/cpexcel.js' f…
pingkunga Jun 9, 2026
698b47c
feat: override npm path
pingkunga Jun 9, 2026
a019dd3
feat: add UI buildinvset mock
pingkunga Jun 21, 2026
2a8d354
feat: remove interface to SuppportConfigCustom.d.ts
pingkunga Jun 21, 2026
603abd3
doc update readme
pingkunga Jun 21, 2026
0b7b00a
feat: move logic about Support config to Nuxt Composable
pingkunga Jun 22, 2026
bbef7a6
feat: move to helper (composable useSupportConfig)
pingkunga Jun 22, 2026
9f5d81c
feat: add field Select Assignee
pingkunga Jun 22, 2026
6d04832
feat: add SupportConfig
pingkunga Jun 22, 2026
282ca19
feat: fixed container read json issue
pingkunga Jun 23, 2026
37419d0
fixbug revert not working
pingkunga Jun 23, 2026
4afd925
feat: add log
pingkunga Jun 23, 2026
fa15db1
feat: change form readFile to call server api
pingkunga Jun 23, 2026
8f4ac18
feat: move to project version object
pingkunga Jun 24, 2026
58844b7
feat: data mock
pingkunga Jun 24, 2026
34882a7
feat: ย้าย project + Select Assignee มาใส่ส่วน .NET (Primary)
pingkunga Jun 24, 2026
c92813f
feat: Gateway (Optional)
pingkunga Jun 24, 2026
dcf1795
feat: refactor prokject / member
pingkunga Jun 24, 2026
8fb94e5
feat: init template
pingkunga Jun 26, 2026
a16d0d7
feat: add replace value field
pingkunga Jun 26, 2026
e92e2bb
feat: load layout from json
pingkunga Jun 26, 2026
cdaebe8
feat: update template
pingkunga Jun 27, 2026
0446333
feat: add backend api
pingkunga Jun 27, 2026
f0e6fc4
feat: client call api
pingkunga Jun 27, 2026
6e234f9
feat: change tracker > trackerid and fixed type to number
pingkunga Jun 27, 2026
0d458b1
feat: move to use UForm
pingkunga Jun 28, 2026
8726fe3
feat: add endDate
pingkunga Jun 28, 2026
43f35b1
feat: add field this week release for auto field version current branch
pingkunga Jun 28, 2026
0e3a6b4
feat: default start date
pingkunga Jun 28, 2026
8af920a
feat: clear default date
pingkunga Jun 28, 2026
e87e42b
feat: Add Save as Template Feature
pingkunga Jun 28, 2026
143992a
feat: autoremove style when user not select option
pingkunga Jun 29, 2026
4c03eae
feat: add use you own redmine token
pingkunga Jun 29, 2026
c4563b6
feat: change
pingkunga Jun 29, 2026
3d1f709
feat: add schema validation on version screen
pingkunga Jun 30, 2026
8bff4d4
feat: remove default value on layout field
pingkunga Jun 30, 2026
841926b
feat: show redmine link if create success
pingkunga Jun 30, 2026
e4dbece
feat: fix layout name
pingkunga Jun 30, 2026
a77421a
feat: add link to redmine version list on redmine
pingkunga Jun 30, 2026
51e48d4
feat: add driver.js for guide end user
pingkunga Jun 30, 2026
41f0186
feat: tracker title support - in [] such as ds-com / add product tour
pingkunga Jul 1, 2026
e1e43af
feat: add product tour for build common
pingkunga Jul 1, 2026
dde53ca
feat: filter open version on devtracker / build request
pingkunga Jul 2, 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
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
node_modules
.output
.nuxt
dist
Dockerfile*
docker-compose*
.dockerignore
Expand Down
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
registry=https://registry.npmjs.org/
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ WORKDIR /app
# this will cache them and speed up future builds
FROM base AS install
COPY package.json bun.lock ./
COPY vendor ./vendor
COPY . .
ENV npm_config_optional=true
ENV npm_config_platform=linux
Expand Down
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
- [x] List Issue from versions
- [x] New Dev Tracker (Program Spec / Defect / Feature)
- [x] Set your own access Token
- [ ] New Build Tracker (Build Request)
- [x] New Build Common Tracker (Build Request)
- [x] Add Unit Test (Initial)
- [x] Add API admin/release/thisweek-release to get current week release data (Version with due date in current week) and send to Line Notify
- [x] Add API admin/release/send-release-mail to send current week release data to specific email address
- [x] Add Admin Page to manage Release Notii Email Template (Subject / Body) with some variable such as {{versionName}} / {{versionDueDate}} / {{versionIssues}} (List of Issues in Version with name and tracker type)
- [x] Add Simple Auth with password (No Register / No User Management) to protect Admin Page
- [x] Add API to List Branches from GitLab and Cache in Memory with Incremental Sync (Pull Latest 100 Events and Merge to Cache)
- [x] Add Admin Page to List Branches with Pagination (PER_PAGE = 5) and Search (Search by Branch Name)

# Plan List

Expand All @@ -29,6 +31,7 @@
- Refactor Code eq. remove hardcode to config such as List Versions (On Specific Project Id) / Tracker Template with some hardcode id of custom field
- Add MCP
- Add Chat
- Add Build Dotnet Set Build Tracker with some custom field such as Target Version / Git Branch / Build Status

# Nuxt Minimal Starter

Expand Down Expand Up @@ -116,14 +119,14 @@ bun add axios
## Build & Run

```
docker build --pull -t bun-redmine:0.3.0rc15 .
docker build --pull -t bun-redmine:0.3.3rc9 .

docker build --pull -t bun-redmine:0.3.0rc15 . --no-cache --progress=plain
docker build --pull -t bun-redmine:0.3.3rc9 . --no-cache --progress=plain

docker run -d -p 3000:3000 --env-file .\.env --name bun-redmine bun-redmine:0.3.0rc15
docker run -d -p 3000:3000 --env-file .\.env --name bun-redmine bun-redmine:0.3.3rc9

docker tag bun-redmine:0.3.0rc15 pingkunga/bun-redmine:0.3.0rc15
docker push pingkunga/bun-redmine:0.3.0rc15
docker tag bun-redmine:0.3.3rc9 pingkunga/bun-redmine:0.3.3rc9
docker push pingkunga/bun-redmine:0.3.3rc9
```

## Test
Expand Down
36 changes: 36 additions & 0 deletions app/components/devtrackers/ClientAccessKey.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<template>
<ClientOnly>
<!--check if found access key in local storage-->
<div v-if="!accessKey" class="text-red-500 mb-4">
Please set your access key in Client Setting
</div>
<div v-else class="mb-2 space-y-2 pt-2">
<UFormField label="Encrypt Access Key">
<UInput
:model-value="accessKey"
readonly
type="password"
icon="i-heroicons-lock-closed"
/>
</UFormField>
<div class="flex items-center gap-6">
<USwitch
:model-value="isUseServerToken"
@update:model-value="$emit('update:isUseServerToken', $event)"
/>
<span class="text-sm text-gray-700 dark:text-gray-200">Use Server Token</span>
</div>
</div>
</ClientOnly>
</template>

<script setup lang="ts">
defineProps<{
accessKey: string | null;
isUseServerToken: boolean;
}>();

defineEmits<{
(e: 'update:isUseServerToken', value: boolean): void;
}>();
</script>
40 changes: 38 additions & 2 deletions app/components/sidebar/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@
:key="i"
class="mb-1"
>
<NuxtLink :to="item.to" class="flex items-center p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800">
<NuxtLink
:to="item.to"
class="sidebar-link flex items-center rounded-lg p-2 transition-colors"
:class="{ 'sidebar-link-active': isActive(item.to) }"
>
<UIcon :name="item.icon" class="mr-2" />
{{ item.title }}
<span class="sidebar-title">{{ item.title }}</span>
</NuxtLink>
</li>
<!-- Admin Section -->
Expand All @@ -25,6 +29,10 @@
<UIcon name="i-mdi-email-fast-outline" class="mr-2" />
Release Mail
</NuxtLink>
<NuxtLink to="/admin/branches" class="flex items-center p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 text-primary font-medium mt-1">
<UIcon name="i-mdi-source-branch" class="mr-2" />
GitLab Branches
</NuxtLink>
</li>
</ul>
</div>
Expand All @@ -34,6 +42,15 @@
<script setup lang="ts">
import sidebarItems from "./sidebarItems";
const sidebarMenu = ref(sidebarItems);
const route = useRoute();

const isActive = (path: string) => {
if (path === '/') {
return route.path === '/'
}

return route.path.startsWith(path)
}

const authCookie = useCookie('admin_session')
const isLoggedInCookie = useCookie('is_logged_in')
Expand All @@ -47,6 +64,21 @@ const version = ref(config.public.appVersion || "0.2.0-DEV");
</script>

<style scoped>
.sidebar-link {
color: inherit;
}

.sidebar-link:hover {
background: rgba(29, 158, 117, 0.08);
}

.sidebar-link-active {
background: linear-gradient(135deg, rgba(29, 158, 117, 0.18), rgba(29, 158, 117, 0.08));
color: #1d9e75;
border: 1px solid rgba(29, 158, 117, 0.24);
font-weight: 600;
}

.version-text {
display: flex;
justify-content: center; /* Center horizontally */
Expand All @@ -57,4 +89,8 @@ const version = ref(config.public.appVersion || "0.2.0-DEV");
text-align: center; /* Ensure text alignment */
padding-bottom: 0px;
}

.sidebar-title {
white-space: pre-line; /* honor \n in strings and wrap accordingly */
}
</style>
10 changes: 10 additions & 0 deletions app/components/sidebar/sidebarItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ export default [
icon: "i-mdi-code-block-braces",
to: "/devtrackers",
},
{
title: "New build request\n(NET INV PRODUCT)",
icon: "i-mdi-code-tags-check",
to: "/buildinvset",
},
{
title: "New Build Request\n(NET COMMON)",
icon: "i-mdi-code-tags-check",
to: "/buildnetcommon",
},
{
title: "Client Setting",
icon: "i-mdi-window-shutter-cog",
Expand Down
24 changes: 24 additions & 0 deletions app/composables/useBuildInvSetAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { BuildInvSetRequest } from '~/shared/types/BuildInvSet'

export default function useBuildInvSetAPI() {
const submitBuildInvSet = async (request: BuildInvSetRequest, headers?: Record<string, string>) => {
const { data, error } = await useFetch('/api/buildinvset', {
method: 'POST',
body: JSON.stringify({ BuildInvSetRequest: request }),
headers: headers,
})

if (error.value) {
throw createError({
statusCode: error.value.statusCode ?? 500,
statusMessage: error.value.statusMessage ?? 'Failed to submit Build InvSet request',
})
}

return data.value
}

return {
submitBuildInvSet,
}
}
39 changes: 39 additions & 0 deletions app/composables/useBuildInvSetRelease.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { BuildInvSetRequest } from '~~/shared/types/BuildInvSet'
import type { Version, VersionWithReleaseNotes } from '~~/shared/types/Version'

const formatDateInput = (date: Date) => date.toISOString().split('T')[0]

const getDefaultDateRange = () => {
const today = new Date()
return {
startDate: formatDateInput(today),
endDate: formatDateInput(new Date(today.getFullYear(), today.getMonth(), today.getDate() + 4)),
}
}

export const clearThisWeekReleaseSelection = (formState: Pick<BuildInvSetRequest, 'targetVersion' | 'buildBranch' | 'startDate' | 'endDate'>) => {
formState.targetVersion = undefined
formState.buildBranch = ''
const { startDate, endDate } = getDefaultDateRange()
formState.startDate = startDate
formState.endDate = endDate
}

export const applyThisWeekReleaseSelection = (
formState: Pick<BuildInvSetRequest, 'targetVersion' | 'buildBranch' | 'startDate' | 'endDate'>,
release: VersionWithReleaseNotes | null | undefined,
) => {
if (!release) {
clearThisWeekReleaseSelection(formState)
return
}

const releaseDate = release.due_date ? new Date(release.due_date) : new Date()
const startDate = formatDateInput(releaseDate)
const endDate = formatDateInput(new Date(releaseDate.getFullYear(), releaseDate.getMonth(), releaseDate.getDate() + 4))

formState.targetVersion = release as Version
formState.buildBranch = release.currentReleaseBranch || ''
formState.startDate = startDate
formState.endDate = endDate
}
91 changes: 91 additions & 0 deletions app/composables/useBuildInvSetTour.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { driver } from 'driver.js'
import 'driver.js/dist/driver.css'

export default function useBuildInvSetTour() {
const startTour = () => {
const driverObj = driver({
showProgress: true,
animate: true,
steps: [
{
element: '#tour-title-section',
popover: {
title: 'Build Request — Primary',
description: 'Welcome! This page allows you to create comprehensive build-request issues in Redmine for .NET projects, including optional Gateway and VB6 subtasks.',
side: 'bottom',
align: 'start',
},
},
{
element: '#tour-access-key',
popover: {
title: 'Authentication',
description: 'Enter your Redmine Access Key here. You can also toggle "Use Server Token" if you want to use the pre-configured system token instead of your own.',
side: 'bottom',
align: 'start',
},
},
{
element: '#tour-build-info',
popover: {
title: 'Build Details',
description: 'Select your layout (template), tracker, target version, and build branch. Use "Thisweek Release" to auto-fill current release information.',
side: 'bottom',
align: 'start',
},
},
{
element: '#tour-dotnet-options',
popover: {
title: '.NET Options',
description: 'This is the primary build group. Configure flags like GEN_SBOM, SEND_NOTIFY, and CLEANUP_WS for Windows and Container builds.',
side: 'top',
align: 'start',
},
},
{
element: '#tour-gateway-options',
popover: {
title: 'Gateway Subtask',
description: 'Enable this switch if your release needs a Gateway (Spring Boot) subtask created alongside the primary .NET build.',
side: 'top',
align: 'start',
},
},
{
element: '#tour-vb6-options',
popover: {
title: 'VB6 Subtask',
description: 'Include a VB6 build subtask if needed by enabling this section.',
side: 'top',
align: 'start',
},
},
{
element: '#tour-template-save',
popover: {
title: 'Save Template',
description: 'Check this to save your current configuration as a layout template for future use.',
side: 'top',
align: 'start',
},
},
{
element: '#tour-submit-actions',
popover: {
title: 'Final Step',
description: 'Click Submit to create the issues in Redmine. All results and links will be displayed in a toast message upon completion.',
side: 'top',
align: 'center',
},
},
],
})

driverObj.drive()
}

return {
startTour,
}
}
Loading
Loading