Skip to content

Commit 18c84b3

Browse files
modify DashboardRepoList component to show DashboardRepoGroup instead of flat repo list
1 parent 0dc36bc commit 18c84b3

File tree

1 file changed

+126
-40
lines changed

1 file changed

+126
-40
lines changed

web_src/js/components/DashboardRepoList.vue

Lines changed: 126 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,74 @@
11
<script lang="ts">
2-
import {nextTick, defineComponent} from 'vue';
2+
import {nextTick, defineComponent, computed, ref, type Ref} from 'vue';
33
import {SvgIcon} from '../svg.ts';
44
import {GET} from '../modules/fetch.ts';
55
import {fomanticQuery} from '../modules/fomantic/base.ts';
6+
import DashboardRepoGroup from './DashboardRepoGroup.vue';
67
78
const {appSubUrl, assetUrlPrefix, pageData} = window.config;
89
9-
type CommitStatus = 'pending' | 'success' | 'error' | 'failure' | 'warning' | 'skipped';
10+
export type CommitStatus = 'pending' | 'success' | 'error' | 'failure' | 'warning' | 'skipped';
1011
11-
type CommitStatusMap = {
12-
[status in CommitStatus]: {
12+
export type CommitStatusMap = {
13+
[ status in CommitStatus ]: {
1314
name: string,
1415
color: string,
1516
};
1617
};
1718
1819
// make sure this matches templates/repo/commit_status.tmpl
19-
const commitStatus: CommitStatusMap = {
20+
export const commitStatus: CommitStatusMap = {
2021
pending: {name: 'octicon-dot-fill', color: 'yellow'},
2122
success: {name: 'octicon-check', color: 'green'},
2223
error: {name: 'gitea-exclamation', color: 'red'},
2324
failure: {name: 'octicon-x', color: 'red'},
2425
warning: {name: 'gitea-exclamation', color: 'yellow'},
2526
skipped: {name: 'octicon-skip', color: 'grey'},
2627
};
27-
28+
export type GroupMapType = {
29+
repos: any[]
30+
subgroups: number[]
31+
id: number
32+
[ k: string ]: any
33+
}
2834
export default defineComponent({
29-
components: {SvgIcon},
35+
components: {SvgIcon, DashboardRepoGroup},
36+
provide() {
37+
return {
38+
expandedGroups: computed({
39+
get: () => {
40+
return this.expandedGroups;
41+
},
42+
set: (v) => {
43+
this.expandedGroups = v;
44+
},
45+
}),
46+
searchURL: this.searchURL,
47+
groups: computed({
48+
get: () => {
49+
return this.groups;
50+
},
51+
set: (v) => {
52+
this.groups = v;
53+
},
54+
}),
55+
repos: computed(() => this.computedRepos),
56+
loadedMap: computed({
57+
get: () => {
58+
return this.loadedMap;
59+
},
60+
set: (v) => {
61+
this.loadedMap = v;
62+
},
63+
}),
64+
orgName: this.organizationName,
65+
};
66+
},
67+
setup() {
68+
const groups = ref(new Map<number, GroupMapType>());
69+
const loadedMap = ref(new Map<number, boolean>([[0, true]]));
70+
return {groupsRef: groups, loadedRef: loadedMap};
71+
},
3072
data() {
3173
const params = new URLSearchParams(window.location.search);
3274
const tab = params.get('repo-search-tab') || 'repos';
@@ -39,6 +81,7 @@ export default defineComponent({
3981
return {
4082
tab,
4183
repos: [],
84+
groupData: this.groupsRef,
4285
reposTotalCount: null,
4386
reposFilter,
4487
archivedFilter,
@@ -78,15 +121,15 @@ export default defineComponent({
78121
subUrl: appSubUrl,
79122
...pageData.dashboardRepoList,
80123
activeIndex: -1, // don't select anything at load, first cursor down will select
124+
expandedGroupsRaw: [],
81125
};
82126
},
83-
84127
computed: {
85128
showMoreReposLink() {
86129
return this.repos.length > 0 && this.repos.length < this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`];
87130
},
88131
searchURL() {
89-
return `${this.subUrl}/repo/search?sort=updated&order=desc&uid=${this.uid}&team_id=${this.teamId}&q=${this.searchQuery
132+
return `${this.subUrl}/group/search?sort=updated&order=desc&uid=${this.uid}&team_id=${this.teamId}&q=${this.searchQuery
90133
}&page=${this.page}&limit=${this.searchLimit}&mode=${this.repoTypes[this.reposFilter].searchMode
91134
}${this.archivedFilter === 'archived' ? '&archived=true' : ''}${this.archivedFilter === 'unarchived' ? '&archived=false' : ''
92135
}${this.privateFilter === 'private' ? '&is_private=true' : ''}${this.privateFilter === 'public' ? '&is_private=false' : ''
@@ -107,6 +150,38 @@ export default defineComponent({
107150
checkboxPrivateFilterProps() {
108151
return {checked: this.privateFilter === 'private', indeterminate: this.privateFilter === 'both'};
109152
},
153+
expandedGroups: {
154+
get() {
155+
return this.expandedGroupsRaw;
156+
},
157+
set(val: number[]) {
158+
this.expandedGroupsRaw = val;
159+
},
160+
},
161+
groups: {
162+
get() {
163+
return this.groupData;
164+
},
165+
set(v: Map<number, GroupMapType>) {
166+
for (const [k, val] of v) {
167+
this.groupData.set(k, val);
168+
}
169+
},
170+
},
171+
computedRepos() {
172+
return this.repos;
173+
},
174+
root() {
175+
return [...(this.groups.get(0)?.subgroups ?? []), ...this.repos];
176+
},
177+
loadedMap: {
178+
get() {
179+
return this.loadedRef;
180+
},
181+
set(v: Ref<Map<number, boolean>>) {
182+
this.loadedRef = v;
183+
},
184+
},
110185
},
111186
112187
mounted() {
@@ -136,6 +211,9 @@ export default defineComponent({
136211
changeReposFilter(filter: string) {
137212
this.reposFilter = filter;
138213
this.repos = [];
214+
this.groups = new Map();
215+
this.loadedMap = new Map();
216+
this.expandedGroupsRaw = [];
139217
this.page = 1;
140218
this.counts[`${filter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
141219
this.searchRepos();
@@ -212,6 +290,8 @@ export default defineComponent({
212290
}
213291
this.page = 1;
214292
this.repos = [];
293+
this.groups = new Map();
294+
this.loadedMap = new Map();
215295
this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
216296
this.searchRepos();
217297
},
@@ -227,6 +307,8 @@ export default defineComponent({
227307
this.page = 1;
228308
}
229309
this.repos = [];
310+
this.groups = new Map();
311+
this.loadedMap = new Map();
230312
this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
231313
await this.searchRepos();
232314
},
@@ -235,7 +317,7 @@ export default defineComponent({
235317
this.isLoading = true;
236318
237319
const searchedMode = this.repoTypes[this.reposFilter].searchMode;
238-
const searchedURL = this.searchURL;
320+
const searchedURL = `${this.searchURL}&group_id=-1`;
239321
const searchedQuery = this.searchQuery;
240322
241323
let response, json;
@@ -257,22 +339,40 @@ export default defineComponent({
257339
response = await GET(searchedURL);
258340
json = await response.json();
259341
} catch {
260-
if (searchedURL === this.searchURL) {
342+
if (searchedURL.startsWith(this.searchURL)) {
261343
this.isLoading = false;
262344
}
263345
return;
264346
}
265347
266-
if (searchedURL === this.searchURL) {
267-
this.repos = json.data.map((webSearchRepo: any) => {
348+
if (searchedURL.startsWith(this.searchURL)) {
349+
this.repos = json.data.repos.map((webSearchRepo: any) => {
268350
return {
269351
...webSearchRepo.repository,
270352
latest_commit_status_state: webSearchRepo.latest_commit_status?.State, // if latest_commit_status is null, it means there is no commit status
271353
latest_commit_status_state_link: webSearchRepo.latest_commit_status?.TargetURL,
272354
locale_latest_commit_status_state: webSearchRepo.locale_latest_commit_status,
273355
};
274356
});
275-
const count = Number(response.headers.get('X-Total-Count'));
357+
this.groups.set(0, {
358+
repos: this.repos.filter((a: any) => !a.group_id),
359+
subgroups: json.data.subgroups.map((g: any) => {
360+
return g.group.id;
361+
}),
362+
data: {},
363+
});
364+
for (const g of json.data.subgroups) {
365+
this.groups.set(g.group.id, {
366+
subgroups: g.subgroups.map((h: any) => h.group.id),
367+
repos: g.repos,
368+
...g.group,
369+
latest_commit_status_state: g.latest_commit_status?.State, // if latest_commit_status is null, it means there is no commit status
370+
latest_commit_status_state_link: g.latest_commit_status?.TargetURL,
371+
locale_latest_commit_status_state: g.locale_latest_commit_status,
372+
id: g.group.id,
373+
});
374+
}
375+
const count = this.repos.length;
276376
if (searchedQuery === '' && searchedMode === '' && this.archivedFilter === 'both') {
277377
this.reposTotalCount = count;
278378
}
@@ -372,7 +472,7 @@ export default defineComponent({
372472
<div v-else class="ui attached segment repos-search">
373473
<div class="ui small fluid action left icon input">
374474
<input type="search" spellcheck="false" maxlength="255" @input="changeReposFilter(reposFilter)" v-model="searchQuery" ref="search" @keydown="reposFilterKeyControl" :placeholder="textSearchRepos">
375-
<i class="icon loading-icon-3px" :class="{'is-loading': isLoading}"><svg-icon name="octicon-search" :size="16"/></i>
475+
<i class="icon loading-icon-3px" :class="{ 'is-loading': isLoading }"><svg-icon name="octicon-search" :size="16"/></i>
376476
<div class="ui dropdown icon button" :title="textFilter">
377477
<svg-icon name="octicon-filter" :size="16"/>
378478
<div class="menu">
@@ -401,69 +501,55 @@ export default defineComponent({
401501
</div>
402502
<overflow-menu class="ui secondary pointing tabular borderless menu repos-filter">
403503
<div class="overflow-menu-items tw-justify-center">
404-
<a class="item" tabindex="0" :class="{active: reposFilter === 'all'}" @click="changeReposFilter('all')">
504+
<a class="item" tabindex="0" :class="{ active: reposFilter === 'all' }" @click="changeReposFilter('all')">
405505
{{ textAll }}
406506
<div v-show="reposFilter === 'all'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
407507
</a>
408-
<a class="item" tabindex="0" :class="{active: reposFilter === 'sources'}" @click="changeReposFilter('sources')">
508+
<a class="item" tabindex="0" :class="{ active: reposFilter === 'sources' }" @click="changeReposFilter('sources')">
409509
{{ textSources }}
410510
<div v-show="reposFilter === 'sources'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
411511
</a>
412-
<a class="item" tabindex="0" :class="{active: reposFilter === 'forks'}" @click="changeReposFilter('forks')">
512+
<a class="item" tabindex="0" :class="{ active: reposFilter === 'forks' }" @click="changeReposFilter('forks')">
413513
{{ textForks }}
414514
<div v-show="reposFilter === 'forks'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
415515
</a>
416-
<a class="item" tabindex="0" :class="{active: reposFilter === 'mirrors'}" @click="changeReposFilter('mirrors')" v-if="isMirrorsEnabled">
516+
<a class="item" tabindex="0" :class="{ active: reposFilter === 'mirrors' }" @click="changeReposFilter('mirrors')" v-if="isMirrorsEnabled">
417517
{{ textMirrors }}
418518
<div v-show="reposFilter === 'mirrors'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
419519
</a>
420-
<a class="item" tabindex="0" :class="{active: reposFilter === 'collaborative'}" @click="changeReposFilter('collaborative')">
520+
<a class="item" tabindex="0" :class="{ active: reposFilter === 'collaborative' }" @click="changeReposFilter('collaborative')">
421521
{{ textCollaborative }}
422522
<div v-show="reposFilter === 'collaborative'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
423523
</a>
424524
</div>
425525
</overflow-menu>
426526
</div>
427527
<div v-if="repos.length" class="ui attached table segment tw-rounded-b">
428-
<ul class="repo-owner-name-list">
429-
<li class="tw-flex tw-items-center tw-py-2" v-for="(repo, index) in repos" :class="{'active': index === activeIndex}" :key="repo.id">
430-
<a class="repo-list-link muted" :href="repo.link">
431-
<svg-icon :name="repoIcon(repo)" :size="16" class="repo-list-icon"/>
432-
<div class="text truncate">{{ repo.full_name }}</div>
433-
<div v-if="repo.archived">
434-
<svg-icon name="octicon-archive" :size="16"/>
435-
</div>
436-
</a>
437-
<a class="tw-flex tw-items-center" v-if="repo.latest_commit_status_state" :href="repo.latest_commit_status_state_link || null" :data-tooltip-content="repo.locale_latest_commit_status_state">
438-
<!-- the commit status icon logic is taken from templates/repo/commit_status.tmpl -->
439-
<svg-icon :name="statusIcon(repo.latest_commit_status_state)" :class="'tw-ml-2 commit-status icon text ' + statusColor(repo.latest_commit_status_state)" :size="16"/>
440-
</a>
441-
</li>
442-
</ul>
528+
<dashboard-repo-group :items="root" :depth="1" :cur-group="0" @load-changed="(nv: boolean) => (isLoading = nv)"/>
443529
<div v-if="showMoreReposLink" class="tw-text-center">
444530
<div class="divider tw-my-0"/>
445531
<div class="ui borderless pagination menu narrow tw-my-2">
446532
<a
447-
class="item navigation tw-py-1" :class="{'disabled': page === 1}"
533+
class="item navigation tw-py-1" :class="{ 'disabled': page === 1 }"
448534
@click="changePage(1)" :title="textFirstPage"
449535
>
450536
<svg-icon name="gitea-double-chevron-left" :size="16" class="tw-mr-1"/>
451537
</a>
452538
<a
453-
class="item navigation tw-py-1" :class="{'disabled': page === 1}"
539+
class="item navigation tw-py-1" :class="{ 'disabled': page === 1 }"
454540
@click="changePage(page - 1)" :title="textPreviousPage"
455541
>
456542
<svg-icon name="octicon-chevron-left" :size="16" class="tw-mr-1"/>
457543
</a>
458544
<a class="active item tw-py-1">{{ page }}</a>
459545
<a
460-
class="item navigation" :class="{'disabled': page === finalPage}"
546+
class="item navigation" :class="{ 'disabled': page === finalPage }"
461547
@click="changePage(page + 1)" :title="textNextPage"
462548
>
463549
<svg-icon name="octicon-chevron-right" :size="16" class="tw-ml-1"/>
464550
</a>
465551
<a
466-
class="item navigation tw-py-1" :class="{'disabled': page === finalPage}"
552+
class="item navigation tw-py-1" :class="{ 'disabled': page === finalPage }"
467553
@click="changePage(finalPage)" :title="textLastPage"
468554
>
469555
<svg-icon name="gitea-double-chevron-right" :size="16" class="tw-ml-1"/>
@@ -496,7 +582,7 @@ export default defineComponent({
496582
<div class="text truncate">{{ org.full_name ? `${org.full_name} (${org.name})` : org.name }}</div>
497583
<div><!-- div to prevent underline of label on hover -->
498584
<span class="ui tiny basic label" v-if="org.org_visibility !== 'public'">
499-
{{ org.org_visibility === 'limited' ? textOrgVisibilityLimited: textOrgVisibilityPrivate }}
585+
{{ org.org_visibility === 'limited' ? textOrgVisibilityLimited : textOrgVisibilityPrivate }}
500586
</span>
501587
</div>
502588
</a>

0 commit comments

Comments
 (0)