firstly 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE +18 -0
  3. package/README.md +12 -0
  4. package/esm/KitBaseEnum.d.ts +35 -0
  5. package/esm/KitBaseEnum.js +32 -0
  6. package/esm/KitEntity.d.ts +2 -0
  7. package/esm/KitEntity.js +24 -0
  8. package/esm/KitFields.d.ts +10 -0
  9. package/esm/KitFields.js +196 -0
  10. package/esm/ROUTES.d.ts +88 -0
  11. package/esm/ROUTES.js +98 -0
  12. package/esm/SqlDatabase/LogToConsoleCustom.d.ts +1 -0
  13. package/esm/SqlDatabase/LogToConsoleCustom.js +102 -0
  14. package/esm/api/index.d.ts +42 -0
  15. package/esm/api/index.js +97 -0
  16. package/esm/auth/Adapter.d.ts +10 -0
  17. package/esm/auth/Adapter.js +54 -0
  18. package/esm/auth/AuthController.d.ts +59 -0
  19. package/esm/auth/AuthController.js +434 -0
  20. package/esm/auth/Entities.d.ts +39 -0
  21. package/esm/auth/Entities.js +154 -0
  22. package/esm/auth/RoleController.d.ts +14 -0
  23. package/esm/auth/RoleController.js +57 -0
  24. package/esm/auth/helper.d.ts +1 -0
  25. package/esm/auth/helper.js +7 -0
  26. package/esm/auth/index.d.ts +153 -0
  27. package/esm/auth/index.js +279 -0
  28. package/esm/auth/providers/github.d.ts +25 -0
  29. package/esm/auth/providers/github.js +51 -0
  30. package/esm/auth/providers/index.d.ts +3 -0
  31. package/esm/auth/providers/index.js +26 -0
  32. package/esm/auth/providers/strava.d.ts +25 -0
  33. package/esm/auth/providers/strava.js +51 -0
  34. package/esm/auth/static/assets/Page-BMFREPjF.d.ts +5 -0
  35. package/esm/auth/static/assets/Page-BMFREPjF.js +18 -0
  36. package/esm/auth/static/assets/Page-BMOLAIFx.d.ts +5 -0
  37. package/esm/auth/static/assets/Page-BMOLAIFx.js +1 -0
  38. package/esm/auth/static/assets/Page-BwHye0GW.d.ts +5 -0
  39. package/esm/auth/static/assets/Page-BwHye0GW.js +1 -0
  40. package/esm/auth/static/assets/Page-gV58jf2r.css +1 -0
  41. package/esm/auth/static/assets/index-CKmKKRRL.d.ts +53 -0
  42. package/esm/auth/static/assets/index-CKmKKRRL.js +2 -0
  43. package/esm/auth/static/assets/index-R27C_TlP.css +4 -0
  44. package/esm/auth/static/favicon.svg +79 -0
  45. package/esm/auth/static/index.html +14 -0
  46. package/esm/auth/types.d.ts +33 -0
  47. package/esm/auth/types.js +1 -0
  48. package/esm/bin/cmd.d.ts +1 -0
  49. package/esm/bin/cmd.js +408 -0
  50. package/esm/changeLog/index.d.ts +55 -0
  51. package/esm/changeLog/index.js +179 -0
  52. package/esm/cron/index.d.ts +60 -0
  53. package/esm/cron/index.js +102 -0
  54. package/esm/feedback/FeedbackController.d.ts +30 -0
  55. package/esm/feedback/FeedbackController.js +313 -0
  56. package/esm/feedback/index.d.ts +18 -0
  57. package/esm/feedback/index.js +14 -0
  58. package/esm/feedback/ui/DialogIssue.svelte +102 -0
  59. package/esm/feedback/ui/DialogIssue.svelte.d.ts +20 -0
  60. package/esm/feedback/ui/DialogIssues.svelte +91 -0
  61. package/esm/feedback/ui/DialogIssues.svelte.d.ts +20 -0
  62. package/esm/feedback/ui/DialogMilestones.svelte +38 -0
  63. package/esm/feedback/ui/DialogMilestones.svelte.d.ts +18 -0
  64. package/esm/feedback/ui/Feedback.svelte +12 -0
  65. package/esm/feedback/ui/Feedback.svelte.d.ts +16 -0
  66. package/esm/formats/dates.d.ts +18 -0
  67. package/esm/formats/dates.js +35 -0
  68. package/esm/formats/index.d.ts +4 -0
  69. package/esm/formats/index.js +3 -0
  70. package/esm/formats/numbers.d.ts +4 -0
  71. package/esm/formats/numbers.js +34 -0
  72. package/esm/formats/strings.d.ts +11 -0
  73. package/esm/formats/strings.js +109 -0
  74. package/esm/handle/index.d.ts +7 -0
  75. package/esm/handle/index.js +40 -0
  76. package/esm/helper.d.ts +50 -0
  77. package/esm/helper.js +118 -0
  78. package/esm/index.d.ts +103 -0
  79. package/esm/index.js +42 -0
  80. package/esm/kitCellsBuildor.d.ts +45 -0
  81. package/esm/kitCellsBuildor.js +105 -0
  82. package/esm/kitStoreItem.d.ts +28 -0
  83. package/esm/kitStoreItem.js +170 -0
  84. package/esm/kitStoreList.d.ts +33 -0
  85. package/esm/kitStoreList.js +98 -0
  86. package/esm/mail/index.d.ts +11 -0
  87. package/esm/mail/index.js +51 -0
  88. package/esm/theme.d.ts +4 -0
  89. package/esm/theme.js +4 -0
  90. package/esm/ui/Button.svelte +102 -0
  91. package/esm/ui/Button.svelte.d.ts +27 -0
  92. package/esm/ui/Clipboardable.svelte +19 -0
  93. package/esm/ui/Clipboardable.svelte.d.ts +25 -0
  94. package/esm/ui/Field.svelte +288 -0
  95. package/esm/ui/Field.svelte.d.ts +29 -0
  96. package/esm/ui/FieldGroup.svelte +91 -0
  97. package/esm/ui/FieldGroup.svelte.d.ts +30 -0
  98. package/esm/ui/Grid.svelte +246 -0
  99. package/esm/ui/Grid.svelte.d.ts +46 -0
  100. package/esm/ui/GridLoading.svelte +32 -0
  101. package/esm/ui/GridLoading.svelte.d.ts +20 -0
  102. package/esm/ui/GridPaginate.svelte +66 -0
  103. package/esm/ui/GridPaginate.svelte.d.ts +22 -0
  104. package/esm/ui/Icon.svelte +86 -0
  105. package/esm/ui/Icon.svelte.d.ts +46 -0
  106. package/esm/ui/LibIcon.d.ts +23 -0
  107. package/esm/ui/LibIcon.js +28 -0
  108. package/esm/ui/Loading.svelte +11 -0
  109. package/esm/ui/Loading.svelte.d.ts +20 -0
  110. package/esm/ui/Tooltip.svelte +42 -0
  111. package/esm/ui/Tooltip.svelte.d.ts +22 -0
  112. package/esm/ui/dialog/DialogForm.svelte +70 -0
  113. package/esm/ui/dialog/DialogForm.svelte.d.ts +19 -0
  114. package/esm/ui/dialog/DialogManagement.svelte +87 -0
  115. package/esm/ui/dialog/DialogManagement.svelte.d.ts +25 -0
  116. package/esm/ui/dialog/DialogPrimitive.svelte +89 -0
  117. package/esm/ui/dialog/DialogPrimitive.svelte.d.ts +28 -0
  118. package/esm/ui/dialog/FormEditAction.svelte +54 -0
  119. package/esm/ui/dialog/FormEditAction.svelte.d.ts +24 -0
  120. package/esm/ui/dialog/dialog.d.ts +51 -0
  121. package/esm/ui/dialog/dialog.js +98 -0
  122. package/esm/ui/index.d.ts +5 -0
  123. package/esm/ui/index.js +19 -0
  124. package/esm/ui/internals/FieldContainer.svelte +22 -0
  125. package/esm/ui/internals/FieldContainer.svelte.d.ts +30 -0
  126. package/esm/ui/internals/Input.svelte +98 -0
  127. package/esm/ui/internals/Input.svelte.d.ts +35 -0
  128. package/esm/ui/internals/Textarea.svelte +61 -0
  129. package/esm/ui/internals/Textarea.svelte.d.ts +30 -0
  130. package/esm/ui/internals/select/MultiSelectMelt.svelte +217 -0
  131. package/esm/ui/internals/select/MultiSelectMelt.svelte.d.ts +30 -0
  132. package/esm/ui/internals/select/SelectMelt.svelte +238 -0
  133. package/esm/ui/internals/select/SelectMelt.svelte.d.ts +35 -0
  134. package/esm/ui/internals/select/SelectRadio.svelte +37 -0
  135. package/esm/ui/internals/select/SelectRadio.svelte.d.ts +25 -0
  136. package/esm/ui/link/Link.svelte +28 -0
  137. package/esm/ui/link/Link.svelte.d.ts +25 -0
  138. package/esm/ui/link/LinkPlus.svelte +44 -0
  139. package/esm/ui/link/LinkPlus.svelte.d.ts +21 -0
  140. package/esm/utils/tailwind.d.ts +2 -0
  141. package/esm/utils/tailwind.js +3 -0
  142. package/esm/utils/transition.d.ts +10 -0
  143. package/esm/utils/transition.js +33 -0
  144. package/esm/utils/types.d.ts +17 -0
  145. package/esm/utils/types.js +17 -0
  146. package/esm/virtual/Customer.d.ts +4 -0
  147. package/esm/virtual/Customer.js +24 -0
  148. package/esm/virtual/FilterEntity.d.ts +7 -0
  149. package/esm/virtual/FilterEntity.js +34 -0
  150. package/esm/virtual/StateDemoEnum.d.ts +9 -0
  151. package/esm/virtual/StateDemoEnum.js +42 -0
  152. package/esm/virtual/UIEntity.d.ts +16 -0
  153. package/esm/virtual/UIEntity.js +84 -0
  154. package/esm/vite/index.d.ts +8 -0
  155. package/esm/vite/index.js +47 -0
  156. package/package.json +94 -10
@@ -0,0 +1,102 @@
1
+ import { CronJob } from 'cron';
2
+ import { green, Log, magenta, red, yellow } from '@kitql/helpers';
3
+ const log = new Log('firstly | cron');
4
+ export const jobs = {};
5
+ /**
6
+ * Link to a nice Cheatsheet TODO
7
+ */
8
+ export const cronTime = {
9
+ /**
10
+ * Every morning is actually at 4 am and 7 minutes. (because I like this number!)
11
+ */
12
+ every_morning: '0 7 4 * * *',
13
+ /**
14
+ * Every second
15
+ */
16
+ every_second: '* * * * * *',
17
+ /**
18
+ * Every minute
19
+ */
20
+ every_minute: '0 * * * * *',
21
+ /**
22
+ * Every 10 minute
23
+ */
24
+ every_10_minute: '*/10 * * * *',
25
+ /**
26
+ * Every friday at 5:11 am
27
+ */
28
+ every_friday_morning: '11 5 * * 5',
29
+ };
30
+ /**
31
+ * usage:
32
+ *
33
+ * ```ts
34
+ * import { cron, cronTime } from 'firstly/cron'
35
+ *
36
+ * export const api = firstly({
37
+ * modules: [
38
+ * cron([{
39
+ * topic: 'first_cron',
40
+ * cronTime: cronTime.every_second,
41
+ * onTick: () => { console.log('hello') },
42
+ * start: !dev, // Start in production
43
+ * // runOnInit: dev, // nice in dev environement
44
+ * }])
45
+ * ]
46
+ * })
47
+ * ```
48
+ *
49
+ * using [cron](https://www.npmjs.com/package/cron) library under the hood
50
+ */
51
+ export const cron = (jobsInfos) => {
52
+ return {
53
+ name: 'cron',
54
+ initApi: async () => {
55
+ jobsInfos.forEach((infos) => {
56
+ const { topic, runOnInit, logs, concurrent, ...params } = infos;
57
+ const concurrentToUse = concurrent ?? 1;
58
+ const onTickSaved = params.onTick;
59
+ const fullOnTick = async () => {
60
+ if (jobs[topic].concurrentInProgress < concurrentToUse) {
61
+ jobs[topic].concurrentInProgress = jobs[topic].concurrentInProgress + 1;
62
+ if (logs?.starting === undefined || logs?.starting === true) {
63
+ logJobs(topic, job, 'starting...', false, false);
64
+ }
65
+ // @ts-ignore
66
+ await onTickSaved();
67
+ if (logs?.ended === undefined || logs?.ended === true) {
68
+ logJobs(topic, job, 'done');
69
+ }
70
+ jobs[topic].concurrentInProgress = jobs[topic].concurrentInProgress - 1;
71
+ }
72
+ else {
73
+ logJobs(topic, job, `skipped because of concurrent limit (${yellow(concurrentToUse.toString())})`, false, false);
74
+ }
75
+ };
76
+ params.onTick = fullOnTick;
77
+ const job = CronJob.from(params);
78
+ jobs[topic] = { job, concurrentInProgress: 0 };
79
+ logJobs(topic, job, 'setup done');
80
+ // If not it will be done too early
81
+ if (runOnInit) {
82
+ job.fireOnTick();
83
+ }
84
+ });
85
+ },
86
+ };
87
+ };
88
+ const logJobs = (topic, job, message, with_metadata = true, isSuccess = true) => {
89
+ const l = [];
90
+ l.push(magenta(`[${topic}]`));
91
+ l.push(message);
92
+ if (with_metadata) {
93
+ // If the job is "stopped", there will still be a next date, but it will not fire it. The job has to start.
94
+ l.push(`(${job.running ? green('running') : red('stopped')}, next at ${yellow(job.nextDate().toISO())})`);
95
+ }
96
+ if (isSuccess) {
97
+ log.success(l.join(' '));
98
+ }
99
+ else {
100
+ log.info(l.join(' '));
101
+ }
102
+ };
@@ -0,0 +1,30 @@
1
+ export declare class FeedbackController {
2
+ static getMilestones(): Promise<{
3
+ title: string;
4
+ id: string;
5
+ number: number;
6
+ }[]>;
7
+ static getIssues(milestoneNumber: number, issueState: 'OPEN' | 'CLOSED'): Promise<{
8
+ id: string;
9
+ number: number;
10
+ titleHTML: string;
11
+ state: 'OPEN' | 'CLOSED';
12
+ }[]>;
13
+ static getIssue(issueNumber: number): Promise<{
14
+ id: any;
15
+ state: any;
16
+ items: {
17
+ bodyHTML: string;
18
+ who?: string | undefined;
19
+ createdAt: Date;
20
+ public: boolean;
21
+ }[];
22
+ }>;
23
+ static createIssue(milestoneId: string, title: string, body: string, page: string): Promise<{
24
+ id: string;
25
+ number: number;
26
+ }>;
27
+ static addCommentOnIssue(issueId: string, body: string, page: string): Promise<string>;
28
+ static close(issueId: string): Promise<string>;
29
+ static reOpen(issueId: string): Promise<string>;
30
+ }
@@ -0,0 +1,313 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Allow, BackendMethod, remult } from 'remult';
8
+ import { stry } from '@kitql/helpers';
9
+ import { FEEDBACK_OPTIONS } from '.';
10
+ const GITHUB_GRAPHQL_ENDPOINT = 'https://api.github.com/graphql';
11
+ async function getGitHub(query, variables) {
12
+ if (!FEEDBACK_OPTIONS.GITHUB_API_TOKEN) {
13
+ throw new Error('GITHUB_API_TOKEN not found in .env');
14
+ }
15
+ try {
16
+ const headers = new Headers({
17
+ Authorization: 'Bearer ' + FEEDBACK_OPTIONS.GITHUB_API_TOKEN,
18
+ 'Content-Type': 'application/json',
19
+ });
20
+ const body = stry({ query, variables }, 0);
21
+ const response = await fetch(GITHUB_GRAPHQL_ENDPOINT, { method: 'POST', headers, body });
22
+ const result = await response.json();
23
+ if (result.errors) {
24
+ /* eslint-disable */
25
+ console.error(`result ERRORS`, body, stry(result));
26
+ }
27
+ return result.data;
28
+ }
29
+ catch (error) {
30
+ /* eslint-disable */
31
+ console.error(`error`, error);
32
+ }
33
+ return null;
34
+ }
35
+ async function addMetaData(issueId, author, page) {
36
+ const commentToMinimize = await getGitHub(`mutation AddComment($input: AddCommentInput!) {
37
+ addComment(input: $input) {
38
+ commentEdge {
39
+ node {
40
+ id
41
+ }
42
+ }
43
+ }
44
+ }
45
+ `, {
46
+ input: {
47
+ subjectId: issueId,
48
+ body: `<pre>\n${JSON.stringify({ author, page }, null, 2)}\n</pre>`,
49
+ },
50
+ });
51
+ await getGitHub(`mutation MinimizeComment($input: MinimizeCommentInput!) {
52
+ minimizeComment(input: $input) {
53
+ minimizedComment {
54
+ isMinimized
55
+ }
56
+ }
57
+ }
58
+ `, {
59
+ input: {
60
+ subjectId: commentToMinimize.addComment.commentEdge.node.id,
61
+ classifier: 'OFF_TOPIC',
62
+ },
63
+ });
64
+ }
65
+ export class FeedbackController {
66
+ static async getMilestones() {
67
+ const data = await getGitHub(`query Milestones(
68
+ $repository: String!
69
+ $owner: String!
70
+ $filter: String
71
+ $take: Int = 25
72
+ $cursor: String
73
+ ) {
74
+ repository(name: $repository, owner: $owner) {
75
+ milestones(query: $filter, last: $take, after: $cursor, states: OPEN) {
76
+ pageInfo {
77
+ endCursor
78
+ }
79
+ nodes {
80
+ id
81
+ number
82
+ title
83
+ }
84
+ }
85
+ }
86
+ }
87
+ `, {
88
+ repository: FEEDBACK_OPTIONS.repo.name,
89
+ owner: FEEDBACK_OPTIONS.repo.owner,
90
+ filter: FEEDBACK_OPTIONS.milestones?.title_filter ?? '',
91
+ });
92
+ return data.repository.milestones.nodes.map((c) => {
93
+ return {
94
+ ...c,
95
+ title: c.title.replaceAll(FEEDBACK_OPTIONS.milestones?.title_filter ?? '', '').trim(),
96
+ };
97
+ });
98
+ }
99
+ static async getIssues(milestoneNumber, issueState) {
100
+ const issueOrder = issueState === 'CLOSED'
101
+ ? { field: 'UPDATED_AT', direction: 'DESC' } // When close, the last issue updated.
102
+ : null; // When open take milestone order
103
+ const data = await getGitHub(`query Issues(
104
+ $repository: String!
105
+ $owner: String!
106
+ $filters: IssueFilters
107
+ $milestoneNumber: Int!
108
+ $take: Int = 25
109
+ $cursor: String
110
+ $issueOrder: IssueOrder
111
+ ) {
112
+ repository(name: $repository, owner: $owner) {
113
+ milestone(number: $milestoneNumber) {
114
+ issues(first: $take, after: $cursor, filterBy: $filters, orderBy: $issueOrder) {
115
+ nodes {
116
+ id
117
+ number
118
+ titleHTML
119
+ state
120
+ }
121
+ }
122
+ }
123
+ }
124
+ }
125
+ `, {
126
+ repository: FEEDBACK_OPTIONS.repo.name,
127
+ owner: FEEDBACK_OPTIONS.repo.owner,
128
+ milestoneNumber,
129
+ filters: {
130
+ labels: FEEDBACK_OPTIONS.milestones?.labels_filters ?? [],
131
+ states: [issueState],
132
+ },
133
+ issueOrder,
134
+ });
135
+ return data.repository.milestone.issues.nodes;
136
+ }
137
+ static async getIssue(issueNumber) {
138
+ const data = await getGitHub(`query Issue($repository: String!, $owner: String!, $issueNumber: Int!) {
139
+ repository(name: $repository, owner: $owner) {
140
+ issue(number: $issueNumber) {
141
+ id
142
+ createdAt
143
+ bodyHTML
144
+ state
145
+ comments(first: 100) {
146
+ nodes {
147
+ id
148
+ isMinimized
149
+ createdAt
150
+ body
151
+ bodyHTML
152
+ reactionGroups {
153
+ content
154
+ reactors(first: 1) {
155
+ totalCount
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ }
163
+ `, {
164
+ repository: FEEDBACK_OPTIONS.repo.name,
165
+ owner: FEEDBACK_OPTIONS.repo.owner,
166
+ issueNumber,
167
+ });
168
+ const items = [];
169
+ const firstItem = {
170
+ bodyHTML: data.repository.issue.bodyHTML,
171
+ createdAt: data.repository.issue.createdAt,
172
+ public: true,
173
+ };
174
+ items.push(firstItem);
175
+ const comments = data.repository.issue.comments.nodes;
176
+ for (let i = 0; i < comments.length; i++) {
177
+ if (comments[i].isMinimized) {
178
+ const parsed = JSON.parse(comments[i].body.replaceAll('<pre>\n', '').replaceAll('\n</pre>', ''));
179
+ items[items.length - 1].who = parsed.author;
180
+ items[items.length - 1].public = true;
181
+ }
182
+ else {
183
+ const nbEye = comments[i].reactionGroups.find((c) => c.content === 'EYES')?.reactors
184
+ .totalCount;
185
+ items.push({
186
+ bodyHTML: comments[i].bodyHTML,
187
+ createdAt: new Date(comments[i].createdAt),
188
+ public: nbEye && nbEye > 0 ? true : false,
189
+ });
190
+ }
191
+ }
192
+ const toRet = {
193
+ id: data.repository.issue.id,
194
+ state: data.repository.issue.state,
195
+ items: items.filter((c) => c.public),
196
+ };
197
+ return toRet;
198
+ }
199
+ static async createIssue(milestoneId, title, body, page) {
200
+ const repoInfo = await getGitHub(`query RepoInfo(
201
+ $repository: String!
202
+ $owner: String!
203
+ ) {
204
+ repository(name: $repository, owner: $owner) {
205
+ id
206
+ labels(first: 25){
207
+ nodes{
208
+ id
209
+ name
210
+ }
211
+ }
212
+ }
213
+ }`, {
214
+ repository: FEEDBACK_OPTIONS.repo.name,
215
+ owner: FEEDBACK_OPTIONS.repo.owner,
216
+ });
217
+ const repoInfoData = repoInfo.repository;
218
+ const create_label = repoInfoData.labels.nodes.find((c) => c.name === FEEDBACK_OPTIONS.create_label);
219
+ const newIssue = await getGitHub(`mutation CreateIssue($input: CreateIssueInput!) {
220
+ createIssue(input: $input) {
221
+ issue {
222
+ id
223
+ number
224
+ }
225
+ }
226
+ }
227
+ `, {
228
+ input: {
229
+ repositoryId: repoInfoData.id,
230
+ milestoneId: milestoneId,
231
+ labelIds: [create_label?.id],
232
+ title: title ?? 'New Feedback (wo title...)',
233
+ body: body,
234
+ },
235
+ });
236
+ const toRet = newIssue.createIssue.issue;
237
+ await addMetaData(toRet.id, remult.user?.name, page);
238
+ return toRet;
239
+ }
240
+ static async addCommentOnIssue(issueId, body, page) {
241
+ const input = {
242
+ subjectId: issueId,
243
+ body,
244
+ };
245
+ await getGitHub(`mutation AddComment($input: AddCommentInput!) {
246
+ addComment(input: $input) {
247
+ commentEdge {
248
+ node {
249
+ id
250
+ }
251
+ }
252
+ }
253
+ }
254
+ `, {
255
+ input,
256
+ });
257
+ await addMetaData(issueId, remult.user?.name, page);
258
+ return 'done';
259
+ }
260
+ static async close(issueId) {
261
+ const input = {
262
+ issueId,
263
+ };
264
+ await getGitHub(`mutation CloseIssue($input: CloseIssueInput!) {
265
+ closeIssue(input: $input) {
266
+ issue {
267
+ id
268
+ }
269
+ }
270
+ }
271
+ `, {
272
+ input,
273
+ });
274
+ return 'done';
275
+ }
276
+ static async reOpen(issueId) {
277
+ const input = {
278
+ issueId,
279
+ };
280
+ await getGitHub(`mutation ReOpenIssue($input: ReopenIssueInput!) {
281
+ reopenIssue(input: $input) {
282
+ issue {
283
+ id
284
+ }
285
+ }
286
+ }
287
+ `, {
288
+ input,
289
+ });
290
+ return 'done';
291
+ }
292
+ }
293
+ __decorate([
294
+ BackendMethod({ allowed: Allow.authenticated })
295
+ ], FeedbackController, "getMilestones", null);
296
+ __decorate([
297
+ BackendMethod({ allowed: Allow.authenticated })
298
+ ], FeedbackController, "getIssues", null);
299
+ __decorate([
300
+ BackendMethod({ allowed: Allow.authenticated })
301
+ ], FeedbackController, "getIssue", null);
302
+ __decorate([
303
+ BackendMethod({ allowed: Allow.authenticated })
304
+ ], FeedbackController, "createIssue", null);
305
+ __decorate([
306
+ BackendMethod({ allowed: Allow.authenticated })
307
+ ], FeedbackController, "addCommentOnIssue", null);
308
+ __decorate([
309
+ BackendMethod({ allowed: Allow.authenticated })
310
+ ], FeedbackController, "close", null);
311
+ __decorate([
312
+ BackendMethod({ allowed: Allow.authenticated })
313
+ ], FeedbackController, "reOpen", null);
@@ -0,0 +1,18 @@
1
+ import type { Module } from '../api';
2
+ import { FeedbackController } from './FeedbackController';
3
+ import { default as Feedback } from './ui/Feedback.svelte';
4
+ export { FeedbackController, Feedback };
5
+ type FeedbackOptions = {
6
+ GITHUB_API_TOKEN: string;
7
+ repo: {
8
+ owner: string;
9
+ name: string;
10
+ };
11
+ milestones?: {
12
+ title_filter?: string;
13
+ labels_filters?: string[];
14
+ };
15
+ create_label?: string;
16
+ };
17
+ export declare let FEEDBACK_OPTIONS: FeedbackOptions;
18
+ export declare const feedback: (o: FeedbackOptions) => Module;
@@ -0,0 +1,14 @@
1
+ import { FeedbackController } from './FeedbackController';
2
+ import { default as Feedback } from './ui/Feedback.svelte';
3
+ export { FeedbackController, Feedback };
4
+ export let FEEDBACK_OPTIONS = {
5
+ GITHUB_API_TOKEN: '',
6
+ repo: { owner: '', name: '' },
7
+ };
8
+ export const feedback = (o) => {
9
+ FEEDBACK_OPTIONS = o;
10
+ return {
11
+ name: 'feedback',
12
+ controllers: [FeedbackController],
13
+ };
14
+ };
@@ -0,0 +1,102 @@
1
+ <script>import { onMount } from "svelte";
2
+ import { repo } from "remult";
3
+ import { page } from "$app/stores";
4
+ import { FeedbackController } from "..";
5
+ import { Button, Field, FilterEntity, kitCellBuildor, Loading } from "../..";
6
+ import Textarea from "../../ui/internals/Textarea.svelte";
7
+ export let dialogId;
8
+ const rmvWarning = dialogId;
9
+ export let milestoneId;
10
+ export let issueNumber;
11
+ let state = "loading";
12
+ let issue;
13
+ const update = async () => {
14
+ state = "loading";
15
+ if (issueNumber) {
16
+ issue = await FeedbackController.getIssue(issueNumber);
17
+ }
18
+ state = "done";
19
+ };
20
+ onMount(async () => {
21
+ await update();
22
+ });
23
+ let title;
24
+ let content;
25
+ const send = async () => {
26
+ state = "loading";
27
+ const p = $page.url.pathname + $page.url.search;
28
+ if (!issue?.id) {
29
+ const result = await FeedbackController.createIssue(milestoneId, title, content, p);
30
+ issueNumber = result.number;
31
+ } else {
32
+ await FeedbackController.addCommentOnIssue(issue.id, content, p);
33
+ }
34
+ content = "";
35
+ await update();
36
+ };
37
+ const close = async () => {
38
+ state = "loading";
39
+ await FeedbackController.close(issue.id);
40
+ content = "";
41
+ await update();
42
+ };
43
+ const reOpen = async () => {
44
+ state = "loading";
45
+ await FeedbackController.reOpen(issue.id);
46
+ content = "";
47
+ await update();
48
+ };
49
+ </script>
50
+
51
+ <div class="mb-4 grid gap-4">
52
+ {#if state === 'loading'}
53
+ <Loading class="h-12"></Loading>
54
+ <Loading class="h-12"></Loading>
55
+ <Loading class="h-12"></Loading>
56
+ {:else}
57
+ {#each issue?.items ?? [] as item}
58
+ <div class="chat {item.who ? 'chat-start' : 'chat-end'}">
59
+ <div class="avatar chat-image">
60
+ <div class="w-10 rounded-full">
61
+ <div class="h-10 w-10 {item.who ? 'bg-secondary' : 'bg-primary'}"></div>
62
+ </div>
63
+ </div>
64
+ <div class="chat-header">
65
+ {item.who ?? 'Support'}
66
+ <time class="text-xs opacity-50"
67
+ >{new Date(item.createdAt).toLocaleDateString()} - {new Date(
68
+ item.createdAt,
69
+ ).toLocaleTimeString()}</time
70
+ >
71
+ </div>
72
+ <div class="chat-bubble">{@html item.bodyHTML}</div>
73
+ <!-- <div class="chat-footer opacity-50">Delivered</div> -->
74
+ </div>
75
+ {/each}
76
+
77
+ {#if issueNumber}
78
+ <button on:click={update} class="divider"></button>
79
+ {/if}
80
+
81
+ {#if issue?.state === 'CLOSED'}
82
+ <div class="flex justify-end">
83
+ <Button on:click={reOpen} class="btn-neutral">Re Ouvrir</Button>
84
+ </div>
85
+ {:else}
86
+ {#if issueNumber === null}
87
+ <Field cell={kitCellBuildor(repo(FilterEntity), 'title')} bind:value={title} />
88
+ {/if}
89
+ <Textarea bind:value={content}></Textarea>
90
+ <div class="flex justify-between">
91
+ {#if issueNumber}
92
+ <Button on:click={close} tabIndex={-1} class="btn-outline btn-error"
93
+ >Clore le feedback</Button
94
+ >
95
+ {:else}
96
+ <div></div>
97
+ {/if}
98
+ <Button on:click={send} disabled={content?.length < 3}>Envoyer</Button>
99
+ </div>
100
+ {/if}
101
+ {/if}
102
+ </div>
@@ -0,0 +1,20 @@
1
+ import { SvelteComponent } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ dialogId: number;
5
+ milestoneId: string;
6
+ issueNumber: number | null;
7
+ };
8
+ events: {
9
+ [evt: string]: CustomEvent<any>;
10
+ };
11
+ slots: {};
12
+ exports?: {} | undefined;
13
+ bindings?: string | undefined;
14
+ };
15
+ export type DialogIssueProps = typeof __propDef.props;
16
+ export type DialogIssueEvents = typeof __propDef.events;
17
+ export type DialogIssueSlots = typeof __propDef.slots;
18
+ export default class DialogIssue extends SvelteComponent<DialogIssueProps, DialogIssueEvents, DialogIssueSlots> {
19
+ }
20
+ export {};
@@ -0,0 +1,91 @@
1
+ <script>import { onMount } from "svelte";
2
+ import { FeedbackController } from "..";
3
+ import {
4
+ Button,
5
+ dialog,
6
+ Icon,
7
+ LibIcon_Add,
8
+ LibIcon_Check,
9
+ LibIcon_Search,
10
+ Loading
11
+ } from "../..";
12
+ import DialogIssue from "./DialogIssue.svelte";
13
+ export let dialogId;
14
+ const rmvWarning = dialogId;
15
+ export let milestoneNumber;
16
+ export let milestoneId;
17
+ let state = "loading";
18
+ let issueState = "OPEN";
19
+ let issues = [];
20
+ const update = async (_issueState) => {
21
+ issueState = _issueState;
22
+ state = "loading";
23
+ issues = await FeedbackController.getIssues(milestoneNumber, issueState);
24
+ state = "done";
25
+ };
26
+ onMount(async () => {
27
+ await update(issueState);
28
+ });
29
+ </script>
30
+
31
+ <div class="mb-4 grid gap-4">
32
+ <div class="flex justify-between">
33
+ <div>
34
+ <Button
35
+ class={issueState === 'OPEN' ? 'btn-primary' : 'btn-ghost'}
36
+ on:click={() => update('OPEN')}>En cours</Button
37
+ >
38
+ <Button
39
+ class={issueState === 'CLOSED' ? 'btn-primary' : 'btn-ghost'}
40
+ on:click={() => update('CLOSED')}>Clos</Button
41
+ >
42
+ </div>
43
+
44
+ <Button
45
+ on:click={async () => {
46
+ await dialog.show({
47
+ component: DialogIssue,
48
+ classes: { root: 'overflow-auto w-5/6 h-5/6' },
49
+ props: { issueNumber: null, milestoneId },
50
+ detail: {
51
+ caption: 'Nouveau Feedback',
52
+ icon: { data: LibIcon_Search },
53
+ },
54
+ })
55
+ await update(issueState)
56
+ }}
57
+ >
58
+ <Icon data={LibIcon_Add}></Icon>
59
+ </Button>
60
+ </div>
61
+
62
+ {#each issues as issue}
63
+ <Button
64
+ on:click={async () => {
65
+ await dialog.show({
66
+ component: DialogIssue,
67
+ classes: { root: 'overflow-auto w-5/6 h-5/6' },
68
+ props: { issueNumber: issue.number, milestoneId },
69
+ detail: {
70
+ caption: issue.titleHTML,
71
+ icon: { data: issue.state === 'OPEN' ? LibIcon_Search : LibIcon_Check },
72
+ },
73
+ })
74
+ await update(issueState)
75
+ }}
76
+ class="btn-neutral"
77
+ >
78
+ <div class="w-full text-left">
79
+ {@html issue.titleHTML}
80
+ </div>
81
+ </Button>
82
+ {:else}
83
+ {#if state === 'loading'}
84
+ <Loading class="h-12"></Loading>
85
+ <Loading class="h-12"></Loading>
86
+ <Loading class="h-12"></Loading>
87
+ {:else}
88
+ <p>Nothing here</p>
89
+ {/if}
90
+ {/each}
91
+ </div>