first commit

This commit is contained in:
2025-04-24 13:11:28 +08:00
commit ff9c54d5e4
5960 changed files with 834111 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
import { Tag } from '../../../../../app/src/Features/Tags/types'
import {
GetProjectsResponseBody,
Sort,
} from '../../../../../types/project/dashboard/api'
import { deleteJSON, postJSON } from '../../../infrastructure/fetch-json'
export function getProjects(sortBy: Sort): Promise<GetProjectsResponseBody> {
return postJSON('/api/project', { body: { sort: sortBy } })
}
export function createTag(name: string, color?: string): Promise<Tag> {
return postJSON(`/tag`, {
body: { name, color },
})
}
export function editTag(
tagId: string,
newTagName: string,
newTagColor?: string
) {
return postJSON(`/tag/${tagId}/edit`, {
body: { name: newTagName, color: newTagColor },
})
}
export function deleteTag(tagId: string) {
return deleteJSON(`/tag/${tagId}`)
}
export function addProjectsToTag(tagId: string, projectIds: string[]) {
return postJSON(`/tag/${tagId}/projects`, {
body: {
projectIds,
},
})
}
export function removeProjectFromTag(tagId: string, projectId: string) {
return deleteJSON(`/tag/${tagId}/project/${projectId}`)
}
export function removeProjectsFromTag(tagId: string, projectIds: string[]) {
return postJSON(`/tag/${tagId}/projects/remove`, {
body: {
projectIds,
},
})
}
export function archiveProject(projectId: string) {
return postJSON(`/project/${projectId}/archive`)
}
export function deleteProject(projectId: string) {
return deleteJSON(`/project/${projectId}`)
}
export function leaveProject(projectId: string) {
return postJSON(`/project/${projectId}/leave`)
}
export function renameProject(projectId: string, newName: string) {
return postJSON(`/project/${projectId}/rename`, {
body: {
newProjectName: newName,
},
})
}
export function trashProject(projectId: string) {
return postJSON(`/project/${projectId}/trash`)
}
export function unarchiveProject(projectId: string) {
return deleteJSON(`/project/${projectId}/archive`)
}
export function untrashProject(projectId: string) {
return deleteJSON(`/project/${projectId}/trash`)
}

View File

@@ -0,0 +1,26 @@
import { getUserName } from './user'
import { Project } from '../../../../../types/project/dashboard/api'
export function getOwnerName(project: Project) {
if (project.accessLevel === 'owner') {
return 'You'
}
if (project.owner != null) {
return getUserName(project.owner)
}
return ''
}
export function isDeletableProject(project: Project) {
return project.accessLevel === 'owner' && project.trashed
}
export function isLeavableProject(project: Project) {
return project.accessLevel !== 'owner' && project.trashed
}
export function isArchivedOrTrashed(project: Project) {
return project.archived || project.trashed
}

View File

@@ -0,0 +1,91 @@
import { Project, Sort } from '../../../../../types/project/dashboard/api'
import { SortingOrder } from '../../../../../types/sorting-order'
import { getOwnerName } from './project'
import { Compare } from '../../../../../types/helpers/array/sort'
const order = (order: SortingOrder, projects: Project[]) => {
return order === 'asc' ? [...projects] : projects.reverse()
}
export const ownerNameComparator = (v1: Project, v2: Project) => {
const ownerNameV1 = getOwnerName(v1)
const ownerNameV2 = getOwnerName(v2)
// sorting by owner === 'You' is with highest precedence
if (ownerNameV1 === 'You') {
if (ownerNameV2 === 'You') {
return v1.lastUpdated < v2.lastUpdated
? Compare.SORT_A_BEFORE_B
: Compare.SORT_A_AFTER_B
}
return Compare.SORT_A_AFTER_B
}
// empty owner name
if (ownerNameV1 === '') {
if (ownerNameV2 === '') {
return v1.lastUpdated < v2.lastUpdated
? Compare.SORT_A_BEFORE_B
: Compare.SORT_A_AFTER_B
}
return Compare.SORT_A_BEFORE_B
}
if (ownerNameV2 === 'You') {
return Compare.SORT_A_BEFORE_B
}
if (ownerNameV2 === '') {
return Compare.SORT_A_AFTER_B
}
if (v1.source === 'token') {
return Compare.SORT_A_BEFORE_B
}
if (v2.source === 'token') {
return Compare.SORT_A_AFTER_B
}
return ownerNameV1.localeCompare(ownerNameV2)
}
export const defaultComparator = (
v1: Project,
v2: Project,
key: 'name' | 'lastUpdated'
) => {
const value1 = v1[key].toLowerCase()
const value2 = v2[key].toLowerCase()
if (value1 !== value2) {
return value1 < value2 ? Compare.SORT_A_BEFORE_B : Compare.SORT_A_AFTER_B
}
return Compare.SORT_KEEP_ORDER
}
export default function sortProjects(projects: Project[], sort: Sort) {
let sorted = [...projects]
if (sort.by === 'title') {
sorted = sorted.sort((...args) => {
return defaultComparator(...args, 'name')
})
}
if (sort.by === 'lastUpdated') {
sorted = sorted.sort((...args) => {
return defaultComparator(...args, 'lastUpdated')
})
}
if (sort.by === 'owner') {
sorted = sorted.sort((...args) => {
return ownerNameComparator(...args)
})
}
return order(sort.order, sorted)
}

View File

@@ -0,0 +1,11 @@
import { Tag } from '../../../../../app/src/Features/Tags/types'
import { getHueForId } from '@/shared/utils/colors'
export const MAX_TAG_LENGTH = 50
export function getTagColor(tag?: Tag): string | undefined {
if (!tag) {
return undefined
}
return tag.color || `hsl(${getHueForId(tag._id)}, 70%, 45%)`
}

View File

@@ -0,0 +1,22 @@
import { UserRef } from '../../../../../types/project/dashboard/api'
import getMeta from '@/utils/meta'
export function getUserName(user: UserRef) {
if (user?.id === getMeta('ol-user_id')) {
return 'You'
}
if (user) {
const { firstName, lastName, email } = user
if (firstName || lastName) {
return [firstName, lastName].filter(n => n != null).join(' ')
}
if (email) {
return email
}
}
return 'None'
}