@things-factory/inquiry 0.0.0-stage → 10.1.252
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.
- package/client/bootstrap.ts +27 -0
- package/client/components/inquiry-thread.ts +277 -0
- package/client/components/inquiry-todo.ts +36 -0
- package/client/components/inquiry-writer.ts +212 -0
- package/client/index.ts +2 -0
- package/client/inquiry-watch.ts +45 -0
- package/client/pages/inquiry-list.ts +218 -0
- package/client/route.ts +8 -0
- package/client/tsconfig.json +13 -0
- package/client/utils/inquiry-api.ts +103 -0
- package/client/utils/inquiry-badge.ts +46 -0
- package/client/utils/inquiry-words.ts +26 -0
- package/dist-client/bootstrap.d.ts +2 -0
- package/dist-client/bootstrap.js +25 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/components/inquiry-thread.d.ts +28 -0
- package/dist-client/components/inquiry-thread.js +303 -0
- package/dist-client/components/inquiry-thread.js.map +1 -0
- package/dist-client/components/inquiry-todo.d.ts +9 -0
- package/dist-client/components/inquiry-todo.js +43 -0
- package/dist-client/components/inquiry-todo.js.map +1 -0
- package/dist-client/components/inquiry-writer.d.ts +25 -0
- package/dist-client/components/inquiry-writer.js +233 -0
- package/dist-client/components/inquiry-writer.js.map +1 -0
- package/dist-client/index.d.ts +2 -0
- package/dist-client/index.js +3 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/inquiry-watch.d.ts +2 -0
- package/dist-client/inquiry-watch.js +41 -0
- package/dist-client/inquiry-watch.js.map +1 -0
- package/dist-client/pages/inquiry-list.d.ts +56 -0
- package/dist-client/pages/inquiry-list.js +241 -0
- package/dist-client/pages/inquiry-list.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +9 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-client/utils/inquiry-api.d.ts +73 -0
- package/dist-client/utils/inquiry-api.js +34 -0
- package/dist-client/utils/inquiry-api.js.map +1 -0
- package/dist-client/utils/inquiry-badge.d.ts +4 -0
- package/dist-client/utils/inquiry-badge.js +47 -0
- package/dist-client/utils/inquiry-badge.js.map +1 -0
- package/dist-client/utils/inquiry-words.d.ts +13 -0
- package/dist-client/utils/inquiry-words.js +22 -0
- package/dist-client/utils/inquiry-words.js.map +1 -0
- package/dist-server/attachment-reader.d.ts +1 -0
- package/dist-server/attachment-reader.js +21 -0
- package/dist-server/attachment-reader.js.map +1 -0
- package/dist-server/index.d.ts +2 -0
- package/dist-server/index.js +22 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/inquiry-activity.d.ts +38 -0
- package/dist-server/inquiry-activity.js +38 -0
- package/dist-server/inquiry-activity.js.map +1 -0
- package/dist-server/role-tasks.d.ts +2 -0
- package/dist-server/role-tasks.js +14 -0
- package/dist-server/role-tasks.js.map +1 -0
- package/dist-server/service/index.d.ts +7 -0
- package/dist-server/service/index.js +24 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/inquiry/index.d.ts +4 -0
- package/dist-server/service/inquiry/index.js +8 -0
- package/dist-server/service/inquiry/index.js.map +1 -0
- package/dist-server/service/inquiry/inquiry-resolver.d.ts +29 -0
- package/dist-server/service/inquiry/inquiry-resolver.js +407 -0
- package/dist-server/service/inquiry/inquiry-resolver.js.map +1 -0
- package/dist-server/service/inquiry/inquiry-rules.d.ts +88 -0
- package/dist-server/service/inquiry/inquiry-rules.js +157 -0
- package/dist-server/service/inquiry/inquiry-rules.js.map +1 -0
- package/dist-server/service/inquiry/inquiry-service.d.ts +35 -0
- package/dist-server/service/inquiry/inquiry-service.js +143 -0
- package/dist-server/service/inquiry/inquiry-service.js.map +1 -0
- package/dist-server/service/inquiry/inquiry-types.d.ts +18 -0
- package/dist-server/service/inquiry/inquiry-types.js +69 -0
- package/dist-server/service/inquiry/inquiry-types.js.map +1 -0
- package/dist-server/service/inquiry/inquiry.d.ts +48 -0
- package/dist-server/service/inquiry/inquiry.js +232 -0
- package/dist-server/service/inquiry/inquiry.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +51 -4
- package/server/attachment-reader.ts +18 -0
- package/server/index.ts +18 -0
- package/server/inquiry-activity.ts +35 -0
- package/server/role-tasks.ts +13 -0
- package/server/service/index.ts +19 -0
- package/server/service/inquiry/index.ts +5 -0
- package/server/service/inquiry/inquiry-resolver.ts +321 -0
- package/server/service/inquiry/inquiry-rules.ts +173 -0
- package/server/service/inquiry/inquiry-service.ts +143 -0
- package/server/service/inquiry/inquiry-types.ts +45 -0
- package/server/service/inquiry/inquiry.ts +180 -0
- package/server/tsconfig.json +11 -0
- package/test/inquiry-persistence.test.ts +218 -0
- package/test/inquiry-rules.test.ts +121 -0
- package/test/open-doors.test.ts +44 -0
- package/test/translations.test.ts +53 -0
- package/test/writer-popup-standard.test.ts +40 -0
- package/things-factory.config.js +8 -0
- package/translations/en.json +93 -0
- package/translations/es.json +93 -0
- package/translations/ja.json +93 -0
- package/translations/ko.json +93 -0
- package/translations/ms.json +93 -0
- package/translations/zh.json +93 -0
- package/README.md +0 -3
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The rules of an inquiry (ADR-0184 decisions 1-8), without a database.
|
|
3
|
+
*/
|
|
4
|
+
import {
|
|
5
|
+
answerRefusal,
|
|
6
|
+
badgeOf,
|
|
7
|
+
handlersTurn,
|
|
8
|
+
inputRefusal,
|
|
9
|
+
mayRead,
|
|
10
|
+
moveRefusal,
|
|
11
|
+
newForAsker,
|
|
12
|
+
noticeWordsOf,
|
|
13
|
+
replyRefusal,
|
|
14
|
+
replyRefusalFor,
|
|
15
|
+
sideOf,
|
|
16
|
+
statusAfterReply
|
|
17
|
+
} from '../server/service/inquiry/inquiry-rules'
|
|
18
|
+
|
|
19
|
+
const writer = { userId: 'w', handler: false }
|
|
20
|
+
const handler = { userId: 'h', handler: true }
|
|
21
|
+
const neighbour = { userId: 'n', handler: false }
|
|
22
|
+
const one = (status = 'RECEIVED', extra: any = {}) => ({ kind: 'QUESTION', status, visibility: 'DOMAIN', creatorId: 'w', ...extra })
|
|
23
|
+
|
|
24
|
+
describe('what may be written', () => {
|
|
25
|
+
it('★ a kind of the three and a title', () => {
|
|
26
|
+
expect(inputRefusal({ kind: 'QUESTION', title: '휴가 신청은 어디서?' })).toBeUndefined()
|
|
27
|
+
expect(inputRefusal({ kind: 'COMPLAINT', title: 'x' })).toBe('unknown-kind')
|
|
28
|
+
expect(inputRefusal({ kind: 'BUG', title: ' ' })).toBe('no-title')
|
|
29
|
+
expect(inputRefusal({ kind: 'BUG', title: 'x'.repeat(201) })).toBe('title-too-long')
|
|
30
|
+
expect(inputRefusal({ kind: 'BUG', title: 'x', visibility: 'SECRET' })).toBe('unknown-visibility')
|
|
31
|
+
expect(inputRefusal({ kind: 'BUG', title: 'x', pageUrl: 'h'.repeat(2001) })).toBe('page-url-too-long')
|
|
32
|
+
expect(inputRefusal({ kind: 'BUG', title: 'x', body: 'b'.repeat(20001) })).toBe('body-too-long')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('a reply says something - words or a file', () => {
|
|
36
|
+
expect(replyRefusal(' ', 0)).toBe('empty-reply')
|
|
37
|
+
expect(replyRefusal('', 1)).toBeUndefined()
|
|
38
|
+
expect(replyRefusal('네', 0)).toBeUndefined()
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
describe('who reads and replies', () => {
|
|
43
|
+
it('★ an open one is read by everyone of the domain; a private one by its writer and the handlers', () => {
|
|
44
|
+
expect(mayRead(one(), neighbour)).toBe(true)
|
|
45
|
+
const hidden = one('RECEIVED', { visibility: 'PRIVATE' })
|
|
46
|
+
expect(mayRead(hidden, neighbour)).toBe(false)
|
|
47
|
+
expect(mayRead(hidden, writer)).toBe(true)
|
|
48
|
+
expect(mayRead(hidden, handler)).toBe(true)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('★ replies go between the writer and the handlers, until it is closed', () => {
|
|
52
|
+
expect(replyRefusalFor(one(), writer)).toBeUndefined()
|
|
53
|
+
expect(replyRefusalFor(one(), handler)).toBeUndefined()
|
|
54
|
+
expect(replyRefusalFor(one(), neighbour)).toBe('not-a-party')
|
|
55
|
+
expect(replyRefusalFor(one('CLOSED'), writer)).toBe('closed')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it("the writer's own reply is the asker's, even when the writer handles", () => {
|
|
59
|
+
expect(sideOf(one(), { userId: 'w', handler: true })).toBe('asker')
|
|
60
|
+
expect(sideOf(one(), handler)).toBe('handler')
|
|
61
|
+
expect(answerRefusal('asker', true)).toBe('handlers-only')
|
|
62
|
+
expect(answerRefusal('handler', true)).toBeUndefined()
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
describe('where it stands', () => {
|
|
67
|
+
it('★ a handler answers it, or puts a received one in progress', () => {
|
|
68
|
+
expect(statusAfterReply(one('RECEIVED'), 'handler', true)).toBe('ANSWERED')
|
|
69
|
+
expect(statusAfterReply(one('RECEIVED'), 'handler', false)).toBe('IN_PROGRESS')
|
|
70
|
+
expect(statusAfterReply(one('ANSWERED'), 'handler', false)).toBe('ANSWERED')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('★ the writer replying to an answer or a decision hands it back to the handlers', () => {
|
|
74
|
+
for (const status of ['ANSWERED', 'PLANNED', 'DECLINED']) expect(statusAfterReply(one(status), 'asker', false)).toBe('IN_PROGRESS')
|
|
75
|
+
expect(statusAfterReply(one('RECEIVED'), 'asker', false)).toBe('RECEIVED')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('★ the writer closes their own; the rest is the handlers\'', () => {
|
|
79
|
+
expect(moveRefusal(one('ANSWERED'), 'CLOSED', writer)).toBeUndefined()
|
|
80
|
+
expect(moveRefusal(one('ANSWERED'), 'CLOSED', neighbour)).toBe('handlers-only')
|
|
81
|
+
expect(moveRefusal(one('RECEIVED'), 'ANSWERED', writer)).toBe('handlers-only')
|
|
82
|
+
expect(moveRefusal(one('RECEIVED'), 'IN_PROGRESS', handler)).toBeUndefined()
|
|
83
|
+
expect(moveRefusal(one('CLOSED'), 'IN_PROGRESS', handler)).toBe('closed')
|
|
84
|
+
expect(moveRefusal(one('IN_PROGRESS'), 'RECEIVED', handler)).toBe('not-a-move')
|
|
85
|
+
expect(moveRefusal(one('IN_PROGRESS'), 'IN_PROGRESS', handler)).toBe('same-status')
|
|
86
|
+
expect(moveRefusal(one('IN_PROGRESS'), 'DONE', handler)).toBe('unknown-status')
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('★ planned and declined are a proposal\'s', () => {
|
|
90
|
+
expect(moveRefusal(one('IN_PROGRESS'), 'PLANNED', handler)).toBe('not-a-proposal')
|
|
91
|
+
expect(moveRefusal(one('IN_PROGRESS', { kind: 'PROPOSAL' }), 'PLANNED', handler)).toBeUndefined()
|
|
92
|
+
expect(moveRefusal(one('IN_PROGRESS', { kind: 'PROPOSAL' }), 'DECLINED', handler)).toBeUndefined()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('it waits on the handlers while received or in progress', () => {
|
|
96
|
+
expect(['RECEIVED', 'IN_PROGRESS', 'ANSWERED', 'PLANNED', 'DECLINED', 'CLOSED'].filter(handlersTurn)).toEqual(['RECEIVED', 'IN_PROGRESS'])
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe('the badge', () => {
|
|
101
|
+
it("★ a handler's word is new to the writer until they open it", () => {
|
|
102
|
+
expect(newForAsker(one('ANSWERED'))).toBe(false)
|
|
103
|
+
expect(newForAsker(one('ANSWERED', { handlerActivityAt: '2026-11-04T10:00:00Z' }))).toBe(true)
|
|
104
|
+
expect(newForAsker(one('ANSWERED', { handlerActivityAt: '2026-11-04T10:00:00Z', askerSeenAt: '2026-11-04T09:00:00Z' }))).toBe(true)
|
|
105
|
+
expect(newForAsker(one('ANSWERED', { handlerActivityAt: '2026-11-04T10:00:00Z', askerSeenAt: '2026-11-04T11:00:00Z' }))).toBe(false)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('★ new answers for everyone, and those waiting for a handler', () => {
|
|
109
|
+
expect(badgeOf({ newAnswers: 2, unhandled: 5, handler: false })).toBe(2)
|
|
110
|
+
expect(badgeOf({ newAnswers: 2, unhandled: 5, handler: true })).toBe(7)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
describe('the notice', () => {
|
|
115
|
+
const say = (key: string, english: string, params: Record<string, unknown> = {}) => english.replace(/\{(\w+)\}/g, (_, name) => String(params[name] ?? ''))
|
|
116
|
+
|
|
117
|
+
it('names the kind to the handlers and the move to the writer', () => {
|
|
118
|
+
expect(noticeWordsOf({ title: '엑셀 내보내기', kind: 'PROPOSAL' }, 'received', { body: '목록을 엑셀로' }, say)).toMatchObject({ subject: 'New PROPOSAL: 엑셀 내보내기', summary: '목록을 엑셀로' })
|
|
119
|
+
expect(noticeWordsOf({ title: '엑셀 내보내기', kind: 'PROPOSAL' }, 'moved', { status: 'PLANNED' }, say).subject).toBe('PLANNED: 엑셀 내보내기')
|
|
120
|
+
})
|
|
121
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Every door of inquiry is open on purpose but one: anyone signed in to the domain writes, reads and replies, and each
|
|
3
|
+
* door asks the inquiry itself (ADR-0184 decision 6). Taking one is the handlers' - `inquiry:mutation`, the domain owner
|
|
4
|
+
* admitted.
|
|
5
|
+
*/
|
|
6
|
+
import { gatesOf, openFields } from '../../../test/helpers/open-doors'
|
|
7
|
+
|
|
8
|
+
const ASKS_THE_INQUIRY = 'anyone of the domain - the door asks the inquiry who may (ADR-0184 decision 6)'
|
|
9
|
+
|
|
10
|
+
const OPEN: Record<'Query' | 'Mutation', Record<string, string>> = {
|
|
11
|
+
Query: {
|
|
12
|
+
inquiries: ASKS_THE_INQUIRY,
|
|
13
|
+
inquiry: ASKS_THE_INQUIRY,
|
|
14
|
+
inquirySummary: ASKS_THE_INQUIRY
|
|
15
|
+
},
|
|
16
|
+
Mutation: {
|
|
17
|
+
createInquiry: ASKS_THE_INQUIRY,
|
|
18
|
+
replyInquiry: ASKS_THE_INQUIRY,
|
|
19
|
+
moveInquiry: ASKS_THE_INQUIRY,
|
|
20
|
+
readInquiry: ASKS_THE_INQUIRY
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let gates: Awaited<ReturnType<typeof gatesOf>>
|
|
25
|
+
|
|
26
|
+
beforeAll(async () => {
|
|
27
|
+
gates = await gatesOf('inquiry')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it.each(['Query', 'Mutation'] as const)('★ every open %s door has a written reason', root => {
|
|
31
|
+
expect(openFields(gates[root])).toEqual(Object.keys(OPEN[root]).sort())
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it.each(['Query', 'Mutation'] as const)('★ every other %s door asks for inquiry:mutation and admits the domain owner', root => {
|
|
35
|
+
for (const [field, gate] of Object.entries(gates[root])) {
|
|
36
|
+
if (field in OPEN[root]) continue
|
|
37
|
+
expect({ field, ...gate }).toEqual({ field, category: 'inquiry', privilege: 'mutation', owner: true, super: false })
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('the walk reaches every door', () => {
|
|
42
|
+
expect(Object.keys(gates.Query)).toHaveLength(3)
|
|
43
|
+
expect(Object.keys(gates.Mutation)).toHaveLength(5)
|
|
44
|
+
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Every word inquiries say is in every language the module ships - the keys are strings, so neither tsc nor eslint sees
|
|
3
|
+
* one with no entry. Covers the screens' words, the codes drawn into words (kinds, states, visibility, scopes, moves),
|
|
4
|
+
* the refusals the server says (`inquiry.refusal.<code>`) and the task's words.
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync, readdirSync } from 'fs'
|
|
7
|
+
import { join } from 'path'
|
|
8
|
+
|
|
9
|
+
const ROOT = join(__dirname, '..')
|
|
10
|
+
const LANGUAGES = ['ko', 'en', 'ja', 'zh', 'es', 'ms']
|
|
11
|
+
|
|
12
|
+
const sources = (dir: string): string[] =>
|
|
13
|
+
readdirSync(dir, { withFileTypes: true }).flatMap(entry => (entry.isDirectory() ? sources(join(dir, entry.name)) : /\.ts$/.test(entry.name) && !/\.test\.ts$/.test(entry.name) ? [join(dir, entry.name)] : []))
|
|
14
|
+
|
|
15
|
+
const text = [...sources(join(ROOT, 'client')), ...sources(join(ROOT, 'server'))].map(file => readFileSync(file, 'utf8')).join('\n')
|
|
16
|
+
const locale = (lang: string): Record<string, string> => JSON.parse(readFileSync(join(ROOT, 'translations', `${lang}.json`), 'utf8'))
|
|
17
|
+
const codes = (name: string) => [...new RegExp(`${name} = \\[([^\\]]*)\\]`).exec(text)![1].matchAll(/'([A-Z_]+)'/g)].map(match => match[1])
|
|
18
|
+
|
|
19
|
+
/* a task id is a name, not a word */
|
|
20
|
+
const used = new Set<string>([...text.matchAll(/'(inquiry\.[a-z0-9.-]+[a-z0-9])'/g)].map(match => match[1]).filter(key => key !== 'inquiry.answering'))
|
|
21
|
+
for (const code of codes('INQUIRY_KINDS')) {
|
|
22
|
+
used.add(`inquiry.kind.${code}`)
|
|
23
|
+
used.add(`inquiry.text.kind-hint-${code.toLowerCase()}`)
|
|
24
|
+
used.add(`inquiry.text.body-placeholder-${code.toLowerCase()}`)
|
|
25
|
+
}
|
|
26
|
+
for (const code of codes('INQUIRY_STATUSES')) used.add(`inquiry.status.${code}`)
|
|
27
|
+
for (const code of codes('INQUIRY_STATUSES').filter(code => code !== 'RECEIVED')) used.add(`inquiry.move.${code}`)
|
|
28
|
+
for (const code of codes('INQUIRY_VISIBILITIES')) used.add(`inquiry.visibility.${code}`)
|
|
29
|
+
for (const scope of ['all', 'mine', 'handlers-turn']) used.add(`inquiry.scope.${scope}`)
|
|
30
|
+
for (const kind of ['received', 'replied', 'answered', 'moved', 'asker-replied']) used.add(`inquiry.notice.${kind}-subject`)
|
|
31
|
+
/* The refusals: those the doors name and those the rules return. */
|
|
32
|
+
for (const [, code] of text.matchAll(/refuse\(context, '([a-z-]+)'/g)) used.add(`inquiry.refusal.${code}`)
|
|
33
|
+
const rules = readFileSync(join(ROOT, 'server/service/inquiry/inquiry-rules.ts'), 'utf8')
|
|
34
|
+
for (const [, code] of rules.matchAll(/return '([a-z-]+)'/g)) if (!['asker', 'handler'].includes(code)) used.add(`inquiry.refusal.${code}`)
|
|
35
|
+
used.add('task.inquiry.answering.name')
|
|
36
|
+
used.add('task.inquiry.answering.description')
|
|
37
|
+
|
|
38
|
+
test('the floor - the parser found the words', () => {
|
|
39
|
+
expect(used.size).toBeGreaterThan(80)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test.each(LANGUAGES)('★ every key inquiries name is in %s', lang => {
|
|
43
|
+
const words = locale(lang)
|
|
44
|
+
expect([...used].filter(key => !(key in words))).toEqual([])
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test.each(LANGUAGES)('a placeholder said in Korean is said in %s too, in the house braces', lang => {
|
|
48
|
+
const ko = locale('ko')
|
|
49
|
+
const words = locale(lang)
|
|
50
|
+
const holes = (said: string) => [...said.matchAll(/\{(\w+)\}/g)].map(match => match[1]).sort()
|
|
51
|
+
expect(Object.keys(ko).filter(key => key in words && holes(ko[key]).join() !== holes(words[key]).join())).toEqual([])
|
|
52
|
+
expect(Object.values(words).filter(said => said.includes('{{'))).toEqual([])
|
|
53
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { readFileSync } from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The writer keeps the house shape of a popup (operato-application design/ui/page-standard.md sections 7, 8), as the
|
|
6
|
+
* user management popups do: only the body scrolls, the one way out and the one main action sit in the shared
|
|
7
|
+
* `ox-popup-actions` row, fields are labels above native inputs, and one-of-several is `ox-input-select-buttons`.
|
|
8
|
+
*/
|
|
9
|
+
const source = readFileSync(path.join(__dirname, '..', 'client', 'components', 'inquiry-writer.ts'), 'utf-8')
|
|
10
|
+
|
|
11
|
+
describe('inquiry-writer', () => {
|
|
12
|
+
it('uses the shared popup styles', () => {
|
|
13
|
+
for (const style of ['CommonPopupStyles', 'PopupBodyStyles', 'FormFieldStyles', 'PopupFieldStyles'])
|
|
14
|
+
expect(source.includes(style)).toBe(true)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('scrolls its body only and ends in the shared action row', () => {
|
|
18
|
+
expect(source.includes('<div class="body">')).toBe(true)
|
|
19
|
+
expect(source.includes("import '@operato/popup/ox-popup-actions.js'")).toBe(true)
|
|
20
|
+
expect((source.match(/<ox-popup-actions>/g) || []).length).toBe(1)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('has exactly one way out and one main action', () => {
|
|
24
|
+
expect((source.match(/slot="cancel"/g) || []).length).toBe(1)
|
|
25
|
+
expect((source.match(/slot="confirm"/g) || []).length).toBe(1)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('picks the kind with the shared select buttons, not buttons of its own', () => {
|
|
29
|
+
expect(source.includes('<ox-input-select-buttons')).toBe(true)
|
|
30
|
+
expect(/aria-pressed|class="kinds"|class="buttons"/.test(source)).toBe(false)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('draws no Material field, checkbox or raised button', () => {
|
|
34
|
+
expect(/<md-(filled|outlined)-text-field|<md-checkbox|<md-elevated-button/.test(source)).toBe(false)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('writes no font size of its own', () => {
|
|
38
|
+
expect(/font-size:\s*[\d.]+(rem|px|em)/.test(source)).toBe(false)
|
|
39
|
+
})
|
|
40
|
+
})
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"inquiry.title.list": "Inquiries",
|
|
3
|
+
"inquiry.title.write": "Send an inquiry",
|
|
4
|
+
"inquiry.button.send": "Send an inquiry",
|
|
5
|
+
"inquiry.button.submit": "Send",
|
|
6
|
+
"inquiry.button.cancel": "Cancel",
|
|
7
|
+
"inquiry.button.remove": "Remove",
|
|
8
|
+
"inquiry.button.take": "Take it",
|
|
9
|
+
"inquiry.button.reply": "Reply",
|
|
10
|
+
"inquiry.button.answer": "Send as the answer",
|
|
11
|
+
"inquiry.button.open": "Open the inquiry",
|
|
12
|
+
"inquiry.button.back": "List",
|
|
13
|
+
"inquiry.field.kind": "Kind",
|
|
14
|
+
"inquiry.field.title": "Title",
|
|
15
|
+
"inquiry.field.body": "Details",
|
|
16
|
+
"inquiry.field.status": "Status",
|
|
17
|
+
"inquiry.field.writer": "Writer",
|
|
18
|
+
"inquiry.field.assignee": "Handler",
|
|
19
|
+
"inquiry.field.created-at": "Sent",
|
|
20
|
+
"inquiry.field.page": "Sent from",
|
|
21
|
+
"inquiry.field.app": "Application",
|
|
22
|
+
"inquiry.kind.QUESTION": "Question",
|
|
23
|
+
"inquiry.kind.BUG": "Bug report",
|
|
24
|
+
"inquiry.kind.PROPOSAL": "Feature proposal",
|
|
25
|
+
"inquiry.status.RECEIVED": "Received",
|
|
26
|
+
"inquiry.status.IN_PROGRESS": "In progress",
|
|
27
|
+
"inquiry.status.ANSWERED": "Answered",
|
|
28
|
+
"inquiry.status.PLANNED": "Planned",
|
|
29
|
+
"inquiry.status.DECLINED": "Declined",
|
|
30
|
+
"inquiry.status.CLOSED": "Closed",
|
|
31
|
+
"inquiry.visibility.DOMAIN": "Everyone here",
|
|
32
|
+
"inquiry.visibility.PRIVATE": "Handlers only",
|
|
33
|
+
"inquiry.scope.all": "All",
|
|
34
|
+
"inquiry.scope.mine": "Mine",
|
|
35
|
+
"inquiry.scope.handlers-turn": "To handle",
|
|
36
|
+
"inquiry.move.IN_PROGRESS": "Back to in progress",
|
|
37
|
+
"inquiry.move.ANSWERED": "Mark answered",
|
|
38
|
+
"inquiry.move.PLANNED": "Plan it",
|
|
39
|
+
"inquiry.move.DECLINED": "Decline",
|
|
40
|
+
"inquiry.move.CLOSED": "Close",
|
|
41
|
+
"inquiry.text.kind-hint-question": "Ask how to use the application or how to get something done.",
|
|
42
|
+
"inquiry.text.kind-hint-bug": "Tell what did not work as expected.",
|
|
43
|
+
"inquiry.text.kind-hint-proposal": "Propose a feature or an improvement.",
|
|
44
|
+
"inquiry.text.title-limit": "{count}/{max} characters",
|
|
45
|
+
"inquiry.text.body-placeholder-question": "What were you trying to do, and where did you get stuck?",
|
|
46
|
+
"inquiry.text.body-placeholder-bug": "What did you do, and what happened instead of what you expected?",
|
|
47
|
+
"inquiry.text.body-placeholder-proposal": "In which work would what help?",
|
|
48
|
+
"inquiry.text.private-label": "Handlers only",
|
|
49
|
+
"inquiry.text.private-hint": "Only you and the handlers read it.",
|
|
50
|
+
"inquiry.text.domain-hint": "Everyone here reads it. Only you and the handlers reply.",
|
|
51
|
+
"inquiry.text.page-hint": "Sent from: {page}",
|
|
52
|
+
"inquiry.text.sent": "Sent. When it is answered, you are notified and it shows under Inquiries.",
|
|
53
|
+
"inquiry.text.failed": "It could not be done.",
|
|
54
|
+
"inquiry.text.not-found": "This inquiry is not here, or you may not read it.",
|
|
55
|
+
"inquiry.text.assignee": "Handled by {name}",
|
|
56
|
+
"inquiry.text.handler": "Handler",
|
|
57
|
+
"inquiry.text.answer": "Answer",
|
|
58
|
+
"inquiry.text.moved": "Moved to {status}",
|
|
59
|
+
"inquiry.text.closed-note": "This inquiry is closed. Send a new one to ask more.",
|
|
60
|
+
"inquiry.text.reply-placeholder": "Ask more or say something about the answer. A reply to an answer or a decision goes back to the handlers.",
|
|
61
|
+
"inquiry.text.reply-placeholder-handler": "Reply to the writer. Sending it as the answer marks the inquiry answered.",
|
|
62
|
+
"inquiry.text.move-hint": "When you change the status, what is written in the box stays with it as the reason.",
|
|
63
|
+
"inquiry.text.todo-body": "An inquiry waits on the handlers. Answering it, deciding the proposal or closing it ends this to-do.",
|
|
64
|
+
"inquiry.text.scope": "Show",
|
|
65
|
+
"inquiry.text.empty": "No inquiries",
|
|
66
|
+
"inquiry.text.empty-description": "Send what you want to ask, what is broken or what you wish for from 'Send an inquiry' in the avatar menu at the top right.",
|
|
67
|
+
"inquiry.text.new": "New reply",
|
|
68
|
+
"inquiry.notice.received-subject": "New {kind}: {title}",
|
|
69
|
+
"inquiry.notice.replied-subject": "A reply to your {kind}: {title}",
|
|
70
|
+
"inquiry.notice.answered-subject": "Answered: {title}",
|
|
71
|
+
"inquiry.notice.moved-subject": "{status}: {title}",
|
|
72
|
+
"inquiry.notice.asker-replied-subject": "The writer replied: {title}",
|
|
73
|
+
"inquiry.notice.action": "Open the inquiry",
|
|
74
|
+
"inquiry.notice.note": "Open Inquiries in the application to read it.",
|
|
75
|
+
"inquiry.refusal.unknown-kind": "Choose a kind: question, bug report or feature proposal.",
|
|
76
|
+
"inquiry.refusal.no-title": "Write a title.",
|
|
77
|
+
"inquiry.refusal.title-too-long": "The title is at most 200 characters.",
|
|
78
|
+
"inquiry.refusal.body-too-long": "The text is at most 20,000 characters.",
|
|
79
|
+
"inquiry.refusal.unknown-visibility": "It is read by everyone here or by the handlers only.",
|
|
80
|
+
"inquiry.refusal.page-url-too-long": "The address of the screen is longer than 2,000 characters.",
|
|
81
|
+
"inquiry.refusal.empty-reply": "Write something or add a file.",
|
|
82
|
+
"inquiry.refusal.not-a-party": "Only the writer and the handlers reply.",
|
|
83
|
+
"inquiry.refusal.closed": "This inquiry is closed.",
|
|
84
|
+
"inquiry.refusal.handlers-only": "Only a handler may do this.",
|
|
85
|
+
"inquiry.refusal.unknown-status": "There is no such status.",
|
|
86
|
+
"inquiry.refusal.same-status": "It is in that status already.",
|
|
87
|
+
"inquiry.refusal.not-a-move": "An inquiry does not go back to received.",
|
|
88
|
+
"inquiry.refusal.not-a-proposal": "Planned and declined are for feature proposals only.",
|
|
89
|
+
"inquiry.refusal.unknown-inquiry": "This inquiry is not here, or you may not read it.",
|
|
90
|
+
"inquiry.refusal.unknown-scope": "The list is narrowed to all, mine or to handle only.",
|
|
91
|
+
"task.inquiry.answering.name": "Handling inquiries",
|
|
92
|
+
"task.inquiry.answering.description": "Takes and answers questions, bug reports and feature proposals, decides whether a proposal is planned, and closes them. Reads the private ones too."
|
|
93
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"inquiry.title.list": "Consultas",
|
|
3
|
+
"inquiry.title.write": "Enviar una consulta",
|
|
4
|
+
"inquiry.button.send": "Enviar una consulta",
|
|
5
|
+
"inquiry.button.submit": "Enviar",
|
|
6
|
+
"inquiry.button.cancel": "Cancelar",
|
|
7
|
+
"inquiry.button.remove": "Quitar",
|
|
8
|
+
"inquiry.button.take": "Asumir",
|
|
9
|
+
"inquiry.button.reply": "Responder",
|
|
10
|
+
"inquiry.button.answer": "Enviar como respuesta",
|
|
11
|
+
"inquiry.button.open": "Abrir la consulta",
|
|
12
|
+
"inquiry.button.back": "Lista",
|
|
13
|
+
"inquiry.field.kind": "Tipo",
|
|
14
|
+
"inquiry.field.title": "Título",
|
|
15
|
+
"inquiry.field.body": "Detalle",
|
|
16
|
+
"inquiry.field.status": "Estado",
|
|
17
|
+
"inquiry.field.writer": "Autor",
|
|
18
|
+
"inquiry.field.assignee": "Responsable",
|
|
19
|
+
"inquiry.field.created-at": "Enviado",
|
|
20
|
+
"inquiry.field.page": "Enviado desde",
|
|
21
|
+
"inquiry.field.app": "Aplicación",
|
|
22
|
+
"inquiry.kind.QUESTION": "Pregunta",
|
|
23
|
+
"inquiry.kind.BUG": "Informe de error",
|
|
24
|
+
"inquiry.kind.PROPOSAL": "Propuesta de función",
|
|
25
|
+
"inquiry.status.RECEIVED": "Recibida",
|
|
26
|
+
"inquiry.status.IN_PROGRESS": "En curso",
|
|
27
|
+
"inquiry.status.ANSWERED": "Respondida",
|
|
28
|
+
"inquiry.status.PLANNED": "Planificada",
|
|
29
|
+
"inquiry.status.DECLINED": "Descartada",
|
|
30
|
+
"inquiry.status.CLOSED": "Cerrada",
|
|
31
|
+
"inquiry.visibility.DOMAIN": "Todos",
|
|
32
|
+
"inquiry.visibility.PRIVATE": "Solo responsables",
|
|
33
|
+
"inquiry.scope.all": "Todas",
|
|
34
|
+
"inquiry.scope.mine": "Mías",
|
|
35
|
+
"inquiry.scope.handlers-turn": "Por atender",
|
|
36
|
+
"inquiry.move.IN_PROGRESS": "Volver a en curso",
|
|
37
|
+
"inquiry.move.ANSWERED": "Marcar respondida",
|
|
38
|
+
"inquiry.move.PLANNED": "Planificar",
|
|
39
|
+
"inquiry.move.DECLINED": "Descartar",
|
|
40
|
+
"inquiry.move.CLOSED": "Cerrar",
|
|
41
|
+
"inquiry.text.kind-hint-question": "Pregunte cómo usar la aplicación o cómo hacer algo.",
|
|
42
|
+
"inquiry.text.kind-hint-bug": "Informe de lo que no funcionó como se esperaba.",
|
|
43
|
+
"inquiry.text.kind-hint-proposal": "Proponga una función o una mejora.",
|
|
44
|
+
"inquiry.text.title-limit": "{count}/{max} caracteres",
|
|
45
|
+
"inquiry.text.body-placeholder-question": "¿Qué intentaba hacer y dónde se atascó?",
|
|
46
|
+
"inquiry.text.body-placeholder-bug": "¿Qué hizo y qué pasó en lugar de lo esperado?",
|
|
47
|
+
"inquiry.text.body-placeholder-proposal": "¿En qué trabajo ayudaría qué?",
|
|
48
|
+
"inquiry.text.private-label": "Solo los responsables",
|
|
49
|
+
"inquiry.text.private-hint": "Solo usted y los responsables la leen.",
|
|
50
|
+
"inquiry.text.domain-hint": "Lo lee todo el mundo aquí. Solo usted y los responsables responden.",
|
|
51
|
+
"inquiry.text.page-hint": "Enviado desde: {page}",
|
|
52
|
+
"inquiry.text.sent": "Enviada. Cuando se responda, recibirá un aviso y aparecerá en Consultas.",
|
|
53
|
+
"inquiry.text.failed": "No se pudo hacer.",
|
|
54
|
+
"inquiry.text.not-found": "Esta consulta no existe o no puede leerla.",
|
|
55
|
+
"inquiry.text.assignee": "Responsable {name}",
|
|
56
|
+
"inquiry.text.handler": "Responsable",
|
|
57
|
+
"inquiry.text.answer": "Respuesta",
|
|
58
|
+
"inquiry.text.moved": "Cambiada a {status}",
|
|
59
|
+
"inquiry.text.closed-note": "Esta consulta está cerrada. Envíe una nueva para preguntar más.",
|
|
60
|
+
"inquiry.text.reply-placeholder": "Pregunte más o comente la respuesta. Responder a una respuesta o decisión la devuelve a los responsables.",
|
|
61
|
+
"inquiry.text.reply-placeholder-handler": "Responda al autor. Enviarla como respuesta la marca como respondida.",
|
|
62
|
+
"inquiry.text.move-hint": "Al cambiar el estado, lo escrito en el cuadro queda como motivo.",
|
|
63
|
+
"inquiry.text.todo-body": "Una consulta espera a los responsables. Responderla, decidir la propuesta o cerrarla termina esta tarea.",
|
|
64
|
+
"inquiry.text.scope": "Mostrar",
|
|
65
|
+
"inquiry.text.empty": "No hay consultas",
|
|
66
|
+
"inquiry.text.empty-description": "Envíe lo que quiera preguntar, lo que falla o lo que desea desde 'Enviar una consulta' en el menú del avatar arriba a la derecha.",
|
|
67
|
+
"inquiry.text.new": "Nueva respuesta",
|
|
68
|
+
"inquiry.notice.received-subject": "Nueva {kind}: {title}",
|
|
69
|
+
"inquiry.notice.replied-subject": "Respuesta a su {kind}: {title}",
|
|
70
|
+
"inquiry.notice.answered-subject": "Respondida: {title}",
|
|
71
|
+
"inquiry.notice.moved-subject": "{status}: {title}",
|
|
72
|
+
"inquiry.notice.asker-replied-subject": "El autor respondió: {title}",
|
|
73
|
+
"inquiry.notice.action": "Abrir la consulta",
|
|
74
|
+
"inquiry.notice.note": "Ábrala en Consultas de la aplicación.",
|
|
75
|
+
"inquiry.refusal.unknown-kind": "Elija un tipo: pregunta, informe de error o propuesta.",
|
|
76
|
+
"inquiry.refusal.no-title": "Escriba un título.",
|
|
77
|
+
"inquiry.refusal.title-too-long": "El título admite 200 caracteres como máximo.",
|
|
78
|
+
"inquiry.refusal.body-too-long": "El texto admite 20.000 caracteres como máximo.",
|
|
79
|
+
"inquiry.refusal.unknown-visibility": "La leen todos o solo los responsables.",
|
|
80
|
+
"inquiry.refusal.page-url-too-long": "La dirección de la pantalla supera los 2.000 caracteres.",
|
|
81
|
+
"inquiry.refusal.empty-reply": "Escriba algo o adjunte un archivo.",
|
|
82
|
+
"inquiry.refusal.not-a-party": "Solo responden el autor y los responsables.",
|
|
83
|
+
"inquiry.refusal.closed": "Esta consulta está cerrada.",
|
|
84
|
+
"inquiry.refusal.handlers-only": "Solo un responsable puede hacerlo.",
|
|
85
|
+
"inquiry.refusal.unknown-status": "No existe ese estado.",
|
|
86
|
+
"inquiry.refusal.same-status": "Ya está en ese estado.",
|
|
87
|
+
"inquiry.refusal.not-a-move": "No vuelve a recibida.",
|
|
88
|
+
"inquiry.refusal.not-a-proposal": "Planificada y descartada son solo para propuestas.",
|
|
89
|
+
"inquiry.refusal.unknown-inquiry": "Esta consulta no existe o no puede leerla.",
|
|
90
|
+
"inquiry.refusal.unknown-scope": "La lista solo se filtra por todas, mías o por atender.",
|
|
91
|
+
"task.inquiry.answering.name": "Atender consultas",
|
|
92
|
+
"task.inquiry.answering.description": "Asume y responde preguntas, informes de error y propuestas, decide si una propuesta se planifica y las cierra. También lee las privadas."
|
|
93
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"inquiry.title.list": "お問い合わせ・提案",
|
|
3
|
+
"inquiry.title.write": "お問い合わせ・提案を送る",
|
|
4
|
+
"inquiry.button.send": "お問い合わせ・提案を送る",
|
|
5
|
+
"inquiry.button.submit": "送信",
|
|
6
|
+
"inquiry.button.cancel": "キャンセル",
|
|
7
|
+
"inquiry.button.remove": "外す",
|
|
8
|
+
"inquiry.button.take": "担当する",
|
|
9
|
+
"inquiry.button.reply": "返信",
|
|
10
|
+
"inquiry.button.answer": "回答として送る",
|
|
11
|
+
"inquiry.button.open": "問い合わせを開く",
|
|
12
|
+
"inquiry.button.back": "一覧",
|
|
13
|
+
"inquiry.field.kind": "種類",
|
|
14
|
+
"inquiry.field.title": "件名",
|
|
15
|
+
"inquiry.field.body": "内容",
|
|
16
|
+
"inquiry.field.status": "状態",
|
|
17
|
+
"inquiry.field.writer": "作成者",
|
|
18
|
+
"inquiry.field.assignee": "担当者",
|
|
19
|
+
"inquiry.field.created-at": "送信日時",
|
|
20
|
+
"inquiry.field.page": "送信元の画面",
|
|
21
|
+
"inquiry.field.app": "アプリ",
|
|
22
|
+
"inquiry.kind.QUESTION": "質問",
|
|
23
|
+
"inquiry.kind.BUG": "不具合報告",
|
|
24
|
+
"inquiry.kind.PROPOSAL": "機能提案",
|
|
25
|
+
"inquiry.status.RECEIVED": "受付",
|
|
26
|
+
"inquiry.status.IN_PROGRESS": "対応中",
|
|
27
|
+
"inquiry.status.ANSWERED": "回答済み",
|
|
28
|
+
"inquiry.status.PLANNED": "反映予定",
|
|
29
|
+
"inquiry.status.DECLINED": "見送り",
|
|
30
|
+
"inquiry.status.CLOSED": "完了",
|
|
31
|
+
"inquiry.visibility.DOMAIN": "全員に公開",
|
|
32
|
+
"inquiry.visibility.PRIVATE": "担当者のみ",
|
|
33
|
+
"inquiry.scope.all": "すべて",
|
|
34
|
+
"inquiry.scope.mine": "自分が送ったもの",
|
|
35
|
+
"inquiry.scope.handlers-turn": "対応待ち",
|
|
36
|
+
"inquiry.move.IN_PROGRESS": "対応中に戻す",
|
|
37
|
+
"inquiry.move.ANSWERED": "回答済みにする",
|
|
38
|
+
"inquiry.move.PLANNED": "反映予定にする",
|
|
39
|
+
"inquiry.move.DECLINED": "見送る",
|
|
40
|
+
"inquiry.move.CLOSED": "完了にする",
|
|
41
|
+
"inquiry.text.kind-hint-question": "使い方や業務の進め方を尋ねます。",
|
|
42
|
+
"inquiry.text.kind-hint-bug": "期待どおりに動かなかったことを知らせます。",
|
|
43
|
+
"inquiry.text.kind-hint-proposal": "あると良い機能や改善点を提案します。",
|
|
44
|
+
"inquiry.text.title-limit": "{count}/{max}文字",
|
|
45
|
+
"inquiry.text.body-placeholder-question": "何をしようとして、どこで詰まったかを書いてください。",
|
|
46
|
+
"inquiry.text.body-placeholder-bug": "何をしたとき、何が期待と違ったかを書いてください。",
|
|
47
|
+
"inquiry.text.body-placeholder-proposal": "どの業務で何があると良いかを書いてください。",
|
|
48
|
+
"inquiry.text.private-label": "担当者のみに公開",
|
|
49
|
+
"inquiry.text.private-hint": "自分と担当者だけが読めます。",
|
|
50
|
+
"inquiry.text.domain-hint": "事業所の全員が読みます。返信は自分と担当者だけが書きます。",
|
|
51
|
+
"inquiry.text.page-hint": "送信元の画面: {page}",
|
|
52
|
+
"inquiry.text.sent": "送信しました。回答があると通知され、「お問い合わせ・提案」に表示されます。",
|
|
53
|
+
"inquiry.text.failed": "処理できませんでした。",
|
|
54
|
+
"inquiry.text.not-found": "この問い合わせは存在しないか、閲覧できません。",
|
|
55
|
+
"inquiry.text.assignee": "担当 {name}",
|
|
56
|
+
"inquiry.text.handler": "担当者",
|
|
57
|
+
"inquiry.text.answer": "回答",
|
|
58
|
+
"inquiry.text.moved": "{status}に変更",
|
|
59
|
+
"inquiry.text.closed-note": "完了した問い合わせです。追加の質問は新しく送ってください。",
|
|
60
|
+
"inquiry.text.reply-placeholder": "追加の質問や回答への返信を書きます。回答済み・反映予定・見送りへの返信は再び担当者に届きます。",
|
|
61
|
+
"inquiry.text.reply-placeholder-handler": "作成者に返信します。「回答として送る」はこの問い合わせを回答済みにします。",
|
|
62
|
+
"inquiry.text.move-hint": "状態を変えるとき、欄に書いた内容が理由として残ります。",
|
|
63
|
+
"inquiry.text.todo-body": "担当者の回答を待つ問い合わせです。回答・提案の判断・完了でこのタスクは終わります。",
|
|
64
|
+
"inquiry.text.scope": "表示",
|
|
65
|
+
"inquiry.text.empty": "問い合わせはありません",
|
|
66
|
+
"inquiry.text.empty-description": "聞きたいこと、不具合、欲しい機能は右上のアバターメニューの「お問い合わせ・提案を送る」から送ります。",
|
|
67
|
+
"inquiry.text.new": "新しい返信",
|
|
68
|
+
"inquiry.notice.received-subject": "新しい{kind}: {title}",
|
|
69
|
+
"inquiry.notice.replied-subject": "{kind}への返信: {title}",
|
|
70
|
+
"inquiry.notice.answered-subject": "回答: {title}",
|
|
71
|
+
"inquiry.notice.moved-subject": "{status}: {title}",
|
|
72
|
+
"inquiry.notice.asker-replied-subject": "作成者からの返信: {title}",
|
|
73
|
+
"inquiry.notice.action": "問い合わせを開く",
|
|
74
|
+
"inquiry.notice.note": "アプリの「お問い合わせ・提案」で読めます。",
|
|
75
|
+
"inquiry.refusal.unknown-kind": "種類を選んでください(質問・不具合報告・機能提案)。",
|
|
76
|
+
"inquiry.refusal.no-title": "件名を入力してください。",
|
|
77
|
+
"inquiry.refusal.title-too-long": "件名は200文字までです。",
|
|
78
|
+
"inquiry.refusal.body-too-long": "内容は20,000文字までです。",
|
|
79
|
+
"inquiry.refusal.unknown-visibility": "公開範囲は全員または担当者のみです。",
|
|
80
|
+
"inquiry.refusal.page-url-too-long": "画面のアドレスが2,000文字を超えています。",
|
|
81
|
+
"inquiry.refusal.empty-reply": "返信に文章かファイルを入れてください。",
|
|
82
|
+
"inquiry.refusal.not-a-party": "返信できるのは作成者と担当者だけです。",
|
|
83
|
+
"inquiry.refusal.closed": "完了した問い合わせです。",
|
|
84
|
+
"inquiry.refusal.handlers-only": "担当者だけができます。",
|
|
85
|
+
"inquiry.refusal.unknown-status": "存在しない状態です。",
|
|
86
|
+
"inquiry.refusal.same-status": "すでにその状態です。",
|
|
87
|
+
"inquiry.refusal.not-a-move": "受付には戻せません。",
|
|
88
|
+
"inquiry.refusal.not-a-proposal": "反映予定・見送りは機能提案のみです。",
|
|
89
|
+
"inquiry.refusal.unknown-inquiry": "この問い合わせは存在しないか、閲覧できません。",
|
|
90
|
+
"inquiry.refusal.unknown-scope": "一覧はすべて・自分が送ったもの・対応待ちでのみ絞り込めます。",
|
|
91
|
+
"task.inquiry.answering.name": "お問い合わせ・提案の対応",
|
|
92
|
+
"task.inquiry.answering.description": "質問・不具合報告・機能提案を担当して回答し、提案の反映を判断し、完了します。非公開の問い合わせも読めます。"
|
|
93
|
+
}
|