Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
P
problem_rectification
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
David Yang
problem_rectification
Commits
426a2639
Commit
426a2639
authored
Aug 25, 2026
by
David Yang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: support single and multiple audit materials
parent
5206d42b
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
66 additions
and
36 deletions
+66
-36
FileUploadField.jsx
src/components/FileUploadField.jsx
+7
-3
TailwindEditableField.jsx
...s/RecordDetailDrawer/components/TailwindEditableField.jsx
+1
-0
index.jsx
src/pages/RecordDetailDrawer/index.jsx
+42
-17
ProblemRectificationMaterialField.jsx
...fication/components/ProblemRectificationMaterialField.jsx
+0
-0
problem-rectification.business-flow.md
...blem_rectification/problem-rectification.business-flow.md
+6
-6
record-api.md
src/services/record-api.md
+7
-7
subjectsDefinition.md
src/services/subjectsDefinition.md
+3
-3
No files found.
src/components/FileUploadField.jsx
View file @
426a2639
...
@@ -9,6 +9,7 @@ export default function FileUploadField({
...
@@ -9,6 +9,7 @@ export default function FileUploadField({
description
=
'将文件拖到此处,或从本地选择。'
,
description
=
'将文件拖到此处,或从本地选择。'
,
isDisabled
=
false
,
isDisabled
=
false
,
fileName
=
''
,
fileName
=
''
,
multiple
=
false
,
onFilesChange
,
onFilesChange
,
triggerLabel
=
'选择文件'
,
triggerLabel
=
'选择文件'
,
})
{
})
{
...
@@ -29,9 +30,9 @@ export default function FileUploadField({
...
@@ -29,9 +30,9 @@ export default function FileUploadField({
if
(
fileItems
.
length
===
0
)
return
;
if
(
fileItems
.
length
===
0
)
return
;
const
files
=
await
Promise
.
all
(
fileItems
.
map
((
item
)
=>
item
.
getFile
()));
const
files
=
await
Promise
.
all
(
fileItems
.
map
((
item
)
=>
item
.
getFile
()));
emitFiles
(
files
.
slice
(
0
,
1
));
emitFiles
(
multiple
?
files
:
files
.
slice
(
0
,
1
));
},
},
[
emitFiles
],
[
emitFiles
,
multiple
],
);
);
return
(
return
(
...
@@ -44,6 +45,7 @@ export default function FileUploadField({
...
@@ -44,6 +45,7 @@ export default function FileUploadField({
<
DropZone
.
Input
<
DropZone
.
Input
accept=
{
accept
}
accept=
{
accept
}
disabled=
{
isDisabled
}
disabled=
{
isDisabled
}
multiple=
{
multiple
}
onSelect=
{
emitFiles
}
onSelect=
{
emitFiles
}
/>
/>
<
DropZone
.
Icon
>
<
DropZone
.
Icon
>
...
@@ -51,7 +53,9 @@ export default function FileUploadField({
...
@@ -51,7 +53,9 @@ export default function FileUploadField({
</
DropZone
.
Icon
>
</
DropZone
.
Icon
>
<
DropZone
.
Label
>
{
fileName
||
'将文件拖到此处'
}
</
DropZone
.
Label
>
<
DropZone
.
Label
>
{
fileName
||
'将文件拖到此处'
}
</
DropZone
.
Label
>
<
DropZone
.
Description
>
<
DropZone
.
Description
>
{
fileName
?
'已选择文件,可重新选择进行替换。'
:
description
}
{
fileName
?
`已选择${multiple ? '文件,可继续添加。' : '文件,可重新选择进行替换。'}`
:
description
}
</
DropZone
.
Description
>
</
DropZone
.
Description
>
<
DropZone
.
Trigger
isDisabled=
{
isDisabled
}
>
<
DropZone
.
Trigger
isDisabled=
{
isDisabled
}
>
<
Upload
aria
-
hidden=
"true"
/>
<
Upload
aria
-
hidden=
"true"
/>
...
...
src/pages/RecordDetailDrawer/components/TailwindEditableField.jsx
View file @
426a2639
...
@@ -399,6 +399,7 @@ function ControlledFieldEditor({
...
@@ -399,6 +399,7 @@ function ControlledFieldEditor({
<
ProblemRectificationMaterialField
<
ProblemRectificationMaterialField
editSessionId=
{
editSessionId
}
editSessionId=
{
editSessionId
}
fieldPath=
{
fieldPath
}
fieldPath=
{
fieldPath
}
multiple=
{
Boolean
(
fieldDef
?.
multiple
)
}
onChange=
{
onChange
}
onChange=
{
onChange
}
onPendingLocalMaterialChange=
{
onPendingLocalMaterialChange
}
onPendingLocalMaterialChange=
{
onPendingLocalMaterialChange
}
onValidationChange=
{
onValidationChange
}
onValidationChange=
{
onValidationChange
}
...
...
src/pages/RecordDetailDrawer/index.jsx
View file @
426a2639
...
@@ -268,12 +268,33 @@ const RecordDetailDrawer = (props, ref) => {
...
@@ -268,12 +268,33 @@ const RecordDetailDrawer = (props, ref) => {
},
[]);
},
[]);
const
handlePendingLocalMaterialChange
=
useCallback
(
const
handlePendingLocalMaterialChange
=
useCallback
(
(
fieldPath
,
pendingMaterial
,
editSessionId
)
=>
{
(
fieldPath
,
change
,
editSessionId
)
=>
{
if
(
editSessionId
!==
editSessionRef
.
current
)
return
;
if
(
editSessionId
!==
editSessionRef
.
current
)
return
;
const
nextPendingMaterials
=
{
...
pendingLocalMaterialsRef
.
current
};
const
nextPendingMaterials
=
{
...
pendingLocalMaterialsRef
.
current
};
if
(
pendingMaterial
)
{
const
currentFieldMaterials
=
Array
.
isArray
(
nextPendingMaterials
[
fieldPath
])
nextPendingMaterials
[
fieldPath
]
=
pendingMaterial
;
?
nextPendingMaterials
[
fieldPath
]
:
[];
let
nextFieldMaterials
=
currentFieldMaterials
;
if
(
change
?.
type
===
'replace'
)
{
nextFieldMaterials
=
change
.
materials
||
[];
}
else
if
(
change
?.
type
===
'add'
)
{
const
materialsById
=
new
Map
(
currentFieldMaterials
.
map
((
material
)
=>
[
material
.
clientId
,
material
]),
);
(
change
.
materials
||
[]).
forEach
((
material
)
=>
{
materialsById
.
set
(
material
.
clientId
,
material
);
});
nextFieldMaterials
=
[...
materialsById
.
values
()];
}
else
if
(
change
?.
type
===
'remove'
)
{
nextFieldMaterials
=
currentFieldMaterials
.
filter
(
(
material
)
=>
material
.
clientId
!==
change
.
clientId
,
);
}
if
(
nextFieldMaterials
.
length
>
0
)
{
nextPendingMaterials
[
fieldPath
]
=
nextFieldMaterials
;
}
else
{
}
else
{
delete
nextPendingMaterials
[
fieldPath
];
delete
nextPendingMaterials
[
fieldPath
];
}
}
...
@@ -292,20 +313,24 @@ const RecordDetailDrawer = (props, ref) => {
...
@@ -292,20 +313,24 @@ const RecordDetailDrawer = (props, ref) => {
const
pendingMaterials
=
Object
.
entries
(
pendingLocalMaterialsRef
.
current
)
const
pendingMaterials
=
Object
.
entries
(
pendingLocalMaterialsRef
.
current
)
.
filter
(([
fieldPath
])
=>
dirtyFields
[
fieldPath
]);
.
filter
(([
fieldPath
])
=>
dirtyFields
[
fieldPath
]);
for
(
const
[
fieldPath
,
pendingMaterial
]
of
pendingMaterials
)
{
for
(
const
[
fieldPath
,
fieldMaterials
]
of
pendingMaterials
)
{
if
(
pendingMaterial
.
materialRecordId
)
continue
;
for
(
const
pendingMaterial
of
fieldMaterials
)
{
if
(
pendingMaterial
.
materialRecordId
)
continue
;
const
createdMaterial
=
await
createProblemRectificationMaterial
({
fileAsset
:
pendingMaterial
.
fileAsset
,
const
createdMaterial
=
await
createProblemRectificationMaterial
({
currentUser
,
fileAsset
:
pendingMaterial
.
fileAsset
,
});
currentUser
,
pendingLocalMaterialsRef
.
current
=
{
});
...
pendingLocalMaterialsRef
.
current
,
const
currentFieldMaterials
=
pendingLocalMaterialsRef
.
current
[
fieldPath
]
||
[];
[
fieldPath
]:
{
pendingLocalMaterialsRef
.
current
=
{
...
pendingMaterial
,
...
pendingLocalMaterialsRef
.
current
,
materialRecordId
:
createdMaterial
.
id
,
[
fieldPath
]:
currentFieldMaterials
.
map
((
material
)
=>
},
material
.
clientId
===
pendingMaterial
.
clientId
};
?
{
...
material
,
materialRecordId
:
createdMaterial
.
id
}
:
material
,
),
};
}
}
}
await
handleFieldsUpdate
(
Object
.
values
(
dirtyFields
));
await
handleFieldsUpdate
(
Object
.
values
(
dirtyFields
));
...
...
src/pages/problem_rectification/components/ProblemRectificationMaterialField.jsx
View file @
426a2639
This diff is collapsed.
Click to expand it.
src/pages/problem_rectification/problem-rectification.business-flow.md
View file @
426a2639
...
@@ -146,7 +146,7 @@
...
@@ -146,7 +146,7 @@
-
`am_material`
:审计材料中心共享主档。问题清单详情可查询已有主档;从本地上传证明材料并保存详情时同步创建主档
-
`am_material`
:审计材料中心共享主档。问题清单详情可查询已有主档;从本地上传证明材料并保存详情时同步创建主档
问题记录仍在
`metadata.rectification_proof_materials`
保存
`{name,type,size,url}`
文件快照,不保存材料主档
`_id`
,不建立自动联动关系
。
问题记录仍在
`metadata.rectification_proof_materials`
保存
文件快照,不保存材料主档
`_id`
,不建立自动联动关系。字段单值时保存
`{name,type,size,url}`
,字段配置为多值时保存上述文件对象数组
。
### 4.4 流程辅助对象
### 4.4 流程辅助对象
...
@@ -706,7 +706,7 @@
...
@@ -706,7 +706,7 @@
-
原主表中的多值选择
`responsible_department`
在旧实现中被转成逗号拼接文本
-
原主表中的多值选择
`responsible_department`
在旧实现中被转成逗号拼接文本
-
`fill_in_the_people`
、
`review_people`
继续保存为用户
`_id[]`
-
`fill_in_the_people`
、
`review_people`
继续保存为用户
`_id[]`
-
`rectification_proof_materials`
作为文件对象原样保留
-
`rectification_proof_materials`
按线上字段的单值或多值形态保留文件对象或文件对象数组
-
归档是快照,后续原问题记录再变化,不应反向污染归档结果
-
归档是快照,后续原问题记录再变化,不应反向污染归档结果
-
归档明细导入失败时不创建归档批次;归档批次创建失败时应提示用户归档未完整完成,避免出现无法从批次入口追溯的孤立明细
-
归档明细导入失败时不创建归档批次;归档批次创建失败时应提示用户归档未完整完成,避免出现无法从批次入口追溯的孤立明细
...
@@ -756,11 +756,11 @@
...
@@ -756,11 +756,11 @@
整改证明材料交互限定在问题清单详情弹框,不改变整改处理页、整改流程表格和归档详情:
整改证明材料交互限定在问题清单详情弹框,不改变整改处理页、整改流程表格和归档详情:
1.
“材料中心”首次读取当前用户可访问的最近 20 条
`am_material`
,支持按材料标题或文件名远程搜索;选中后仅把
`metadata.file_asset`
快照写入详情草稿。
1.
“材料中心”首次读取当前用户可访问的最近 20 条
`am_material`
,支持按材料标题或文件名远程搜索;选中后仅把
`metadata.file_asset`
快照写入详情草稿。
控件运行时读取字段
`multiple`
:单值选择会替换原附件,多值选择会追加并按文件地址去重,附件可逐项删除。
2.
“本地上传”选择文件后立即调用
`/upload`
,上传期间禁止保存
;成功后将文件对象写入详情草稿,并提示保存后同步到材料中心
。
2.
“本地上传”选择文件后立即调用
`/upload`
,上传期间禁止保存
及修改当前附件;单值只接收一个文件,多值可一次选择或拖入多个文件。批量上传成功项追加到详情草稿,失败项显示文件级汇总错误并允许重新选择
。
3.
用户点击详情“保存”时,
先创建
`am_material`
,写入
`source_app = issue_rectification`
、当前用户
`_id`
和 ISO 上传时间,再
更新
`problem_rectification.metadata.rectification_proof_materials`
。
3.
用户点击详情“保存”时,
为每个本次新增且仍保留在草稿中的本地文件分别创建
`am_material`
,写入
`source_app = issue_rectification`
、当前用户
`_id`
和 ISO 上传时间;所有待建档项成功后,再按单值对象或多值对象数组
更新
`problem_rectification.metadata.rectification_proof_materials`
。
4.
用户取消编辑时不创建材料主档;选择已有材料不重复上传、不创建新主档。
4.
用户取消编辑时不创建材料主档;选择已有材料不重复上传、不创建新主档。
5.
材料创建失败时保留编辑草稿且不更新问题;材料创建成功但问题更新失败时保留已建档标记,再次保存只
重试问题更新,避免重复材料。
5.
任一材料创建失败时保留编辑草稿且不更新问题;同一批次已创建成功的材料,以及材料创建成功但问题更新失败的情况,均保留逐项建档标记,再次保存只创建尚未成功的材料并
重试问题更新,避免重复材料。
6.
清空或替换问题附件不删除材料中心主档或底层文件;已有材料后续修改或软删除不联动历史问题快照。
6.
清空或替换问题附件不删除材料中心主档或底层文件;已有材料后续修改或软删除不联动历史问题快照。
建议字段分组:
建议字段分组:
...
...
src/services/record-api.md
View file @
426a2639
...
@@ -269,7 +269,7 @@ Query:`calculated=true|false`
...
@@ -269,7 +269,7 @@ Query:`calculated=true|false`
## 5. 问题详情与审计材料中心接入
## 5. 问题详情与审计材料中心接入
问题清单详情弹框中的
`metadata.rectification_proof_materials`
支持从统一材料池选择已有文件,或上传本地文件后同步建档。整改处理页、整改流程表格和归档详情不使用本节流程。
问题清单详情弹框中的
`metadata.rectification_proof_materials`
支持从统一材料池选择已有文件,或上传本地文件后同步建档。
控件必须运行时读取 Subject 字段的
`multiple`
配置:单值字段保存一个文件对象,多值字段保存文件对象数组。
整改处理页、整改流程表格和归档详情不使用本节流程。
### 5.1 查询已有材料
### 5.1 查询已有材料
...
@@ -301,12 +301,12 @@ Query:`calculated=true|false`
...
@@ -301,12 +301,12 @@ Query:`calculated=true|false`
}
}
```
```
搜索关键词必须先转义正则特殊字符。选中材料后,只将
`metadata.file_asset`
的
`{name,type,size,url}`
快照写入问题详情草稿,不保存
`am_material`
`_id`
。
搜索关键词必须先转义正则特殊字符。选中材料后,只将
`metadata.file_asset`
的
`{name,type,size,url}`
快照写入问题详情草稿,不保存
`am_material`
`_id`
。
单值字段选择后替换原值;多值字段允许连续选择并追加,按文件地址去重,支持逐项删除。
### 5.2 本地上传并写入材料中心
### 5.2 本地上传并写入材料中心
1.
选择文件后立即调用
`POST /upload`
,将返回结果规范化为
`{name,type,size,url}`
并保留在详情草稿。
1.
选择文件后立即调用
`POST /upload`
,将返回结果规范化为
`{name,type,size,url}`
并保留在详情草稿。
单值字段只接收一个文件并替换原值;多值字段允许一次选择或拖入多个文件,每个文件分别上传,成功项追加到草稿,失败项保留错误提示并可重新选择。
2.
用户点击详情“保存”时调用
`POST /record`
创建材料主档:
2.
用户点击详情“保存”时
,为每个本次本地上传且仍保留在草稿中的文件分别
调用
`POST /record`
创建材料主档:
```
json
```
json
{
{
...
@@ -328,6 +328,6 @@ Query:`calculated=true|false`
...
@@ -328,6 +328,6 @@ Query:`calculated=true|false`
}
}
```
```
3.
材料主档创建成功后,再调用
`PUT /record/:name`
更新问题记录的
`metadata.rectification_proof_materials`
。
3.
所有待建档材料创建成功后,再调用
`PUT /record/:name`
更新问题记录的
`metadata.rectification_proof_materials`
,单值写对象,多值写对象数组
。
4.
材料创建失败时不更新问题记录;材料创建成功但问题更新失败时,当前编辑会话记住已创建的材料,重试保存
不得重复创建。
4.
任一材料创建失败时不更新问题记录;一批材料中已创建成功的条目和“材料已创建、问题更新失败”的条目都在当前编辑会话中保留建档标记,重试保存只处理尚未建档的条目,
不得重复创建。
5.
取消详情编辑不创建
`am_material`
;
已经上传但未建档的二进制文件遵循上传服务自身的清理策略。
5.
用户从草稿逐项删除本地附件后,该附件不再参与材料建档;取消详情编辑不创建
`am_material`
。
已经上传但未建档的二进制文件遵循上传服务自身的清理策略。
src/services/subjectsDefinition.md
View file @
426a2639
...
@@ -78,7 +78,7 @@ type Record = {
...
@@ -78,7 +78,7 @@ type Record = {
-
`number`
:数字
-
`number`
:数字
-
`boolean`
:
`true / false`
-
`boolean`
:
`true / false`
-
`select`
:传选项的存储值,不传显示文案
-
`select`
:传选项的存储值,不传显示文案
-
`file`
:
对象,单文件时建议结构为
`{ name, type, size, url }`
-
`file`
:
单值字段保存
`{ name, type, size, url }`
,
`multiple=true`
时保存上述文件对象数组
-
`object`
:对象或对象数组,前端优先使用结构化表单编辑,无现成交互时再退回 JSON 校验方案
-
`object`
:对象或对象数组,前端优先使用结构化表单编辑,无现成交互时再退回 JSON 校验方案
### 2.4 `reference`
### 2.4 `reference`
...
@@ -187,7 +187,7 @@ type Record = {
...
@@ -187,7 +187,7 @@ type Record = {
-
新增驾驶舱扩展字段当前在线上 Subject 中大多仍为
`text`
类型,前端工作台按标准值引导录入,用于支持后续严重程度、问题分类、问题原因和整改质效分析
-
新增驾驶舱扩展字段当前在线上 Subject 中大多仍为
`text`
类型,前端工作台按标准值引导录入,用于支持后续严重程度、问题分类、问题原因和整改质效分析
-
其余
`select`
类型的可选值必须以线上 Subject 配置中的
`settings.options`
为准,禁止在页面内硬编码
-
其余
`select`
类型的可选值必须以线上 Subject 配置中的
`settings.options`
为准,禁止在页面内硬编码
-
`problem_discovery_situation`
曾在历史文档和归档对象中出现,但本次主对象字段定义未包含;新页面只应在 Subject View 或线上字段配置存在时展示,不能作为
`problem_rectification`
固定字段依赖
-
`problem_discovery_situation`
曾在历史文档和归档对象中出现,但本次主对象字段定义未包含;新页面只应在 Subject View 或线上字段配置存在时展示,不能作为
`problem_rectification`
固定字段依赖
-
问题清单详情弹框中的
`rectification_proof_materials`
支持从
`am_material.metadata.file_asset`
复制文件快照,或上传本地文件后创建
`am_material`
。
问题字段仍只保存
`{name,type,size,url}`
,
不保存材料主档
`_id`
,材料后续修改或软删除不反向联动问题记录
-
问题清单详情弹框中的
`rectification_proof_materials`
支持从
`am_material.metadata.file_asset`
复制文件快照,或上传本地文件后创建
`am_material`
。
控件运行时读取线上字段的
`multiple`
:当前定义为单值时保存
`{name,type,size,url}`
;若后续配置为多值则保存文件对象数组,并支持追加、逐项删除及为每个本地文件分别建档。问题字段
不保存材料主档
`_id`
,材料后续修改或软删除不反向联动问题记录
-
`department_rectification_items`
用于新转发流程的部门级整改明细。无论单条还是多条推送,每个问题都按责任部门生成明细;单条问题即使只有 1 个责任部门,也生成 1 条明细。已推送过的问题再次推送时,必须保留原部门明细记录和
`item_id`
,只在本次参与推送的原明细行上更新填写人、审批人、流程状态和待办 ID;未参与本次推送的原明细行保持不变,不因弹窗删除部门配置而从父记录移除。转发后
`rectification_progress`
、
`rectification_situation`
、
`the_time_of_rectification`
、
`rectification_proof_materials`
、
`audit_status`
、
`final_audit_status`
以明细为事实来源,父字段仅做兼容汇总展示;整改进度、审批状态和最终审批状态按部门明细中最落后的状态汇总,例如“整改中”优先于“已整改”,“未审批”优先于“已审批”
-
`department_rectification_items`
用于新转发流程的部门级整改明细。无论单条还是多条推送,每个问题都按责任部门生成明细;单条问题即使只有 1 个责任部门,也生成 1 条明细。已推送过的问题再次推送时,必须保留原部门明细记录和
`item_id`
,只在本次参与推送的原明细行上更新填写人、审批人、流程状态和待办 ID;未参与本次推送的原明细行保持不变,不因弹窗删除部门配置而从父记录移除。转发后
`rectification_progress`
、
`rectification_situation`
、
`the_time_of_rectification`
、
`rectification_proof_materials`
、
`audit_status`
、
`final_audit_status`
以明细为事实来源,父字段仅做兼容汇总展示;整改进度、审批状态和最终审批状态按部门明细中最落后的状态汇总,例如“整改中”优先于“已整改”,“未审批”优先于“已审批”
-
`problem_rectification_delay_request`
为历史问题级单次延期申请嵌套对象,与
`department_rectification_items`
平级;新发起延期默认按部门写入
`problem_rectification_delay_request_items`
-
`problem_rectification_delay_request`
为历史问题级单次延期申请嵌套对象,与
`department_rectification_items`
平级;新发起延期默认按部门写入
`problem_rectification_delay_request_items`
-
`problem_rectification_delay_request_items`
为部门级延期申请明细数组;每个责任部门 1 条延期申请,独立保存填写人、一级审批人、最终审批人、申请完成时间、延期事由、状态和待办 ID
-
`problem_rectification_delay_request_items`
为部门级延期申请明细数组;每个责任部门 1 条延期申请,独立保存填写人、一级审批人、最终审批人、申请完成时间、延期事由、状态和待办 ID
...
@@ -308,7 +308,7 @@ type Record = {
...
@@ -308,7 +308,7 @@ type Record = {
| 材料分类 |
`archive_category`
| select | 否 | 问题详情不写入 |
| 材料分类 |
`archive_category`
| select | 否 | 问题详情不写入 |
| 所属周期/年份 |
`archive_year`
| text | 否 | 问题详情不写入 |
| 所属周期/年份 |
`archive_year`
| text | 否 | 问题详情不写入 |
问题详情选择已有材料时读取
`material_title`
与
`file_asset`
;搜索范围为
`metadata.material_title`
和
`metadata.file_asset.name`
。列表只展示当前登录用户通过 Record 权限可读取且
`file_asset.url`
有效的记录。
问题详情选择已有材料时读取
`material_title`
与
`file_asset`
;搜索范围为
`metadata.material_title`
和
`metadata.file_asset.name`
。列表只展示当前登录用户通过 Record 权限可读取且
`file_asset.url`
有效的记录。
业务文件字段为多值时,每个本地上传文件仍分别创建一条
`am_material`
,业务记录保存对应的文件快照数组。
## 3.3 问题整改归档明细
## 3.3 问题整改归档明细
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment