@mundogamernetwork/shared-ui 1.16.23 → 1.16.24
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/components/playtest/build-access/BuildAccessPanel.vue +127 -4
- package/components/playtest/build-access/BuildDownload.vue +22 -5
- package/components/playtest/build-access/ReportProblemForm.vue +143 -0
- package/composables/usePlaytestBuildAccess.ts +44 -0
- package/locales/de.json +18 -1
- package/locales/en.json +18 -1
- package/locales/es.json +18 -1
- package/locales/pt-BR.json +18 -1
- package/locales/ro.json +18 -1
- package/package.json +1 -1
- package/services/playtestTesterService.ts +38 -0
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
// role for the (unrelated, accept-time) key/link grant. Renders nothing if
|
|
5
5
|
// the campaign has no ready build asset at all, so campaigns that don't use
|
|
6
6
|
// this feature (concept polls, etc.) show no empty panel.
|
|
7
|
-
import { onMounted, computed } from "vue";
|
|
7
|
+
import { onMounted, computed, ref } from "vue";
|
|
8
8
|
import { usePlaytestBuildAccess } from "../../../composables/usePlaytestBuildAccess";
|
|
9
9
|
import NdaSign from "./NdaSign.vue";
|
|
10
10
|
import BuildDownload from "./BuildDownload.vue";
|
|
11
|
+
import ReportProblemForm from "./ReportProblemForm.vue";
|
|
12
|
+
import type { StoreBuildProblemReportPayload } from "../../../services/playtestTesterService";
|
|
11
13
|
|
|
12
14
|
const props = defineProps<{
|
|
13
15
|
campaignId: number;
|
|
@@ -17,6 +19,13 @@ const state = usePlaytestBuildAccess(props.campaignId);
|
|
|
17
19
|
|
|
18
20
|
const hasAnyBuild = computed(() => state.assets.value.length > 0);
|
|
19
21
|
|
|
22
|
+
// Available at ANY grant status, including pending_nda/revoked — a tester
|
|
23
|
+
// stuck in the sign flow itself (e.g. never got the OTP email) or whose
|
|
24
|
+
// access was pulled still needs a way to reach the studio privately. Never
|
|
25
|
+
// a public wall.
|
|
26
|
+
const showReportForm = ref(false);
|
|
27
|
+
const reportFormRef = ref<InstanceType<typeof ReportProblemForm> | null>(null);
|
|
28
|
+
|
|
20
29
|
onMounted(async () => {
|
|
21
30
|
await state.loadAssets();
|
|
22
31
|
if (hasAnyBuild.value) {
|
|
@@ -28,6 +37,22 @@ async function onDownload(assetId: number) {
|
|
|
28
37
|
const url = await state.getDownloadUrl(assetId);
|
|
29
38
|
if (url) window.open(url, "_blank", "noopener");
|
|
30
39
|
}
|
|
40
|
+
|
|
41
|
+
function openReportForm() {
|
|
42
|
+
state.reportSuccess.value = false;
|
|
43
|
+
showReportForm.value = true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function onReportSubmit(payload: StoreBuildProblemReportPayload) {
|
|
47
|
+
// showReportForm stays true on success — the reportSuccess branch inside
|
|
48
|
+
// the v-else block below is what actually renders the confirmation.
|
|
49
|
+
await state.submitProblemReport(payload);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function onUploadReportMedia(file: File) {
|
|
53
|
+
const url = await state.uploadReportMedia(file);
|
|
54
|
+
reportFormRef.value?.setMediaUrl(url);
|
|
55
|
+
}
|
|
31
56
|
</script>
|
|
32
57
|
|
|
33
58
|
<template>
|
|
@@ -57,9 +82,50 @@ async function onDownload(assetId: number) {
|
|
|
57
82
|
@download="onDownload"
|
|
58
83
|
/>
|
|
59
84
|
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
85
|
+
<div v-else-if="state.grant.value?.status === 'revoked'" class="build-access-panel__revoked">
|
|
86
|
+
<p class="build-access-panel__revoked-title">
|
|
87
|
+
{{ $t("playtest.build_access.revoked", "Your access to this build has been revoked.") }}
|
|
88
|
+
</p>
|
|
89
|
+
<p class="build-access-panel__revoked-hint">
|
|
90
|
+
{{ $t("playtest.build_access.revoked_hint", "This is usually done by the studio running the campaign.") }}
|
|
91
|
+
<button type="button" class="build-access-panel__revoked-link" @click="openReportForm">
|
|
92
|
+
{{ $t("playtest.build_access.revoked_contact_link", "If you think this is a mistake, let them know.") }}
|
|
93
|
+
</button>
|
|
94
|
+
</p>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<div v-if="state.grant.value" class="build-access-panel__contact">
|
|
98
|
+
<button
|
|
99
|
+
v-if="!showReportForm"
|
|
100
|
+
type="button"
|
|
101
|
+
class="build-access-panel__contact-toggle"
|
|
102
|
+
@click="openReportForm"
|
|
103
|
+
>
|
|
104
|
+
{{ $t("playtest.build_access.report_problem", "Report a problem") }}
|
|
105
|
+
</button>
|
|
106
|
+
|
|
107
|
+
<template v-else>
|
|
108
|
+
<div v-if="state.reportSuccess.value" class="build-access-panel__report-sent-wrap">
|
|
109
|
+
<p class="build-access-panel__report-sent">
|
|
110
|
+
{{ $t("playtest.build_access.report_sent", "Sent — only the studio can see this.") }}
|
|
111
|
+
</p>
|
|
112
|
+
<button type="button" class="build-access-panel__contact-toggle" @click="openReportForm">
|
|
113
|
+
{{ $t("playtest.build_access.report_another", "Send another report") }}
|
|
114
|
+
</button>
|
|
115
|
+
</div>
|
|
116
|
+
<template v-else>
|
|
117
|
+
<ReportProblemForm
|
|
118
|
+
ref="reportFormRef"
|
|
119
|
+
:assets="state.assets.value"
|
|
120
|
+
:submitting="state.reportSubmitting.value"
|
|
121
|
+
:uploading="state.reportUploading.value"
|
|
122
|
+
@submit="onReportSubmit"
|
|
123
|
+
@upload-media="onUploadReportMedia"
|
|
124
|
+
/>
|
|
125
|
+
<p v-if="state.reportError.value" class="build-access-panel__report-error">{{ state.reportError.value }}</p>
|
|
126
|
+
</template>
|
|
127
|
+
</template>
|
|
128
|
+
</div>
|
|
63
129
|
</div>
|
|
64
130
|
</template>
|
|
65
131
|
|
|
@@ -83,10 +149,67 @@ async function onDownload(assetId: number) {
|
|
|
83
149
|
}
|
|
84
150
|
|
|
85
151
|
&__revoked {
|
|
152
|
+
display: flex;
|
|
153
|
+
flex-direction: column;
|
|
154
|
+
gap: 6px;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
&__revoked-title {
|
|
86
158
|
font-size: 13px;
|
|
87
159
|
color: var(--danger, #e53935);
|
|
88
160
|
margin: 0;
|
|
89
161
|
}
|
|
162
|
+
|
|
163
|
+
&__revoked-hint {
|
|
164
|
+
font-size: 12px;
|
|
165
|
+
color: var(--secondary-info-fg, #888);
|
|
166
|
+
margin: 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
&__revoked-link {
|
|
170
|
+
background: none;
|
|
171
|
+
border: none;
|
|
172
|
+
padding: 0;
|
|
173
|
+
margin-left: 4px;
|
|
174
|
+
font-size: 12px;
|
|
175
|
+
color: var(--primary, #d297ff);
|
|
176
|
+
cursor: pointer;
|
|
177
|
+
text-decoration: underline;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
&__contact {
|
|
181
|
+
margin-top: 14px;
|
|
182
|
+
padding-top: 14px;
|
|
183
|
+
border-top: 1px solid var(--button-secondary-default-bg);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
&__contact-toggle {
|
|
187
|
+
background: none;
|
|
188
|
+
border: none;
|
|
189
|
+
padding: 0;
|
|
190
|
+
font-size: 12px;
|
|
191
|
+
color: var(--primary, #d297ff);
|
|
192
|
+
cursor: pointer;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
&__report-sent-wrap {
|
|
196
|
+
display: flex;
|
|
197
|
+
flex-direction: column;
|
|
198
|
+
gap: 8px;
|
|
199
|
+
align-items: flex-start;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
&__report-sent {
|
|
203
|
+
font-size: 12px;
|
|
204
|
+
color: var(--primary, #d297ff);
|
|
205
|
+
margin: 0;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
&__report-error {
|
|
209
|
+
font-size: 12px;
|
|
210
|
+
color: var(--danger, #e53935);
|
|
211
|
+
margin: 8px 0 0;
|
|
212
|
+
}
|
|
90
213
|
}
|
|
91
214
|
|
|
92
215
|
@keyframes build-access-panel-pulse {
|
|
@@ -37,11 +37,14 @@ function formatSize(bytes: number | null): string {
|
|
|
37
37
|
|
|
38
38
|
<div v-else class="build-download__list">
|
|
39
39
|
<div v-for="asset in assets" :key="asset.id" class="build-download__row">
|
|
40
|
-
<div class="build-
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
<div class="build-download__main">
|
|
41
|
+
<div class="build-download__info">
|
|
42
|
+
<span class="build-download__platform">{{ asset.platform }}</span>
|
|
43
|
+
<span class="build-download__filename">{{ asset.original_filename }}</span>
|
|
44
|
+
<span v-if="asset.version_label" class="build-download__version">{{ asset.version_label }}</span>
|
|
45
|
+
<span v-if="asset.size_bytes" class="build-download__size">{{ formatSize(asset.size_bytes) }}</span>
|
|
46
|
+
</div>
|
|
47
|
+
<p v-if="asset.notes" class="build-download__notes">{{ asset.notes }}</p>
|
|
45
48
|
</div>
|
|
46
49
|
<button
|
|
47
50
|
type="button"
|
|
@@ -96,6 +99,13 @@ function formatSize(bytes: number | null): string {
|
|
|
96
99
|
border: 1px solid var(--bg-app-badge);
|
|
97
100
|
}
|
|
98
101
|
|
|
102
|
+
&__main {
|
|
103
|
+
display: flex;
|
|
104
|
+
flex-direction: column;
|
|
105
|
+
gap: 4px;
|
|
106
|
+
flex: 1;
|
|
107
|
+
}
|
|
108
|
+
|
|
99
109
|
&__info {
|
|
100
110
|
display: flex;
|
|
101
111
|
flex-wrap: wrap;
|
|
@@ -104,6 +114,13 @@ function formatSize(bytes: number | null): string {
|
|
|
104
114
|
font-size: 13px;
|
|
105
115
|
}
|
|
106
116
|
|
|
117
|
+
&__notes {
|
|
118
|
+
white-space: pre-wrap;
|
|
119
|
+
font-size: 11px;
|
|
120
|
+
color: var(--secondary-info-fg, #888);
|
|
121
|
+
margin: 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
107
124
|
&__platform {
|
|
108
125
|
text-transform: uppercase;
|
|
109
126
|
font-size: 10px;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Private tester -> studio message about a build-access grant — never a
|
|
3
|
+
// public wall. Structural mirror of playtest/lqa/FileIssueForm.vue, minus
|
|
4
|
+
// the LQA-specific issue_type/severity taxonomy and with evidence made
|
|
5
|
+
// OPTIONAL (a question doesn't need a screenshot, unlike a defect report).
|
|
6
|
+
import { ref, computed } from "vue";
|
|
7
|
+
import type { BuildProblemReportCategory, StoreBuildProblemReportPayload, PlaytestBuildAsset } from "../../../services/playtestTesterService";
|
|
8
|
+
|
|
9
|
+
const props = defineProps<{
|
|
10
|
+
assets: PlaytestBuildAsset[];
|
|
11
|
+
submitting?: boolean;
|
|
12
|
+
uploading?: boolean;
|
|
13
|
+
}>();
|
|
14
|
+
|
|
15
|
+
const emit = defineEmits<{
|
|
16
|
+
(e: "submit", payload: StoreBuildProblemReportPayload): void
|
|
17
|
+
(e: "upload-media", file: File): void
|
|
18
|
+
}>();
|
|
19
|
+
|
|
20
|
+
const CATEGORIES: BuildProblemReportCategory[] = ["question", "installation_problem", "access_problem", "other"];
|
|
21
|
+
|
|
22
|
+
const category = ref<BuildProblemReportCategory | "">("");
|
|
23
|
+
const assetId = ref<number | "">("");
|
|
24
|
+
const message = ref("");
|
|
25
|
+
const mediaUrl = ref<string | null>(null);
|
|
26
|
+
|
|
27
|
+
const canSubmit = computed(() =>
|
|
28
|
+
!!category.value &&
|
|
29
|
+
message.value.trim().length > 0 &&
|
|
30
|
+
!props.submitting
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
function onFileChange(e: Event) {
|
|
34
|
+
const file = (e.target as HTMLInputElement).files?.[0];
|
|
35
|
+
if (file) emit("upload-media", file);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function setMediaUrl(url: string | null) {
|
|
39
|
+
mediaUrl.value = url;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
defineExpose({ setMediaUrl });
|
|
43
|
+
|
|
44
|
+
function submit() {
|
|
45
|
+
if (!canSubmit.value || !category.value) return;
|
|
46
|
+
emit("submit", {
|
|
47
|
+
category: category.value,
|
|
48
|
+
message: message.value.trim(),
|
|
49
|
+
playtest_build_asset_id: assetId.value ? Number(assetId.value) : undefined,
|
|
50
|
+
media_url: mediaUrl.value ?? undefined,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
category.value = "";
|
|
54
|
+
assetId.value = "";
|
|
55
|
+
message.value = "";
|
|
56
|
+
mediaUrl.value = null;
|
|
57
|
+
}
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<template>
|
|
61
|
+
<div class="report-problem-form">
|
|
62
|
+
<div class="report-problem-form__row">
|
|
63
|
+
<label class="report-problem-form__label">{{ $t("playtest.build_access.report_category", "What's this about?") }}</label>
|
|
64
|
+
<select v-model="category" class="report-problem-form__select">
|
|
65
|
+
<option value="" disabled>{{ $t("playtest.build_access.select_category_placeholder", "Select…") }}</option>
|
|
66
|
+
<option v-for="c in CATEGORIES" :key="c" :value="c">
|
|
67
|
+
{{ $t(`playtest.build_access.report_category_${c}`) }}
|
|
68
|
+
</option>
|
|
69
|
+
</select>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div v-if="assets.length" class="report-problem-form__row">
|
|
73
|
+
<label class="report-problem-form__label">{{ $t("playtest.build_access.report_related_build", "Related build (optional)") }}</label>
|
|
74
|
+
<select v-model="assetId" class="report-problem-form__select">
|
|
75
|
+
<option value="">{{ $t("playtest.build_access.report_select_build_placeholder", "General question / not build-specific") }}</option>
|
|
76
|
+
<option v-for="asset in assets" :key="asset.id" :value="asset.id">
|
|
77
|
+
{{ asset.platform }} — {{ asset.original_filename }}
|
|
78
|
+
</option>
|
|
79
|
+
</select>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div class="report-problem-form__row">
|
|
83
|
+
<label class="report-problem-form__label">{{ $t("playtest.build_access.report_message", "Message (required)") }}</label>
|
|
84
|
+
<textarea v-model="message" rows="3" class="report-problem-form__textarea" />
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div class="report-problem-form__row">
|
|
88
|
+
<label class="report-problem-form__label">{{ $t("playtest.build_access.report_attach_screenshot", "Attach a screenshot (optional)") }}</label>
|
|
89
|
+
<input type="file" accept="image/*" @change="onFileChange">
|
|
90
|
+
<span v-if="uploading" class="report-problem-form__uploading">{{ $t("playtest.lqa.uploading", "Uploading…") }}</span>
|
|
91
|
+
<span v-else-if="mediaUrl" class="report-problem-form__uploaded">{{ $t("playtest.lqa.uploaded", "Attached") }}</span>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<button type="button" class="btn primary report-problem-form__submit" :disabled="!canSubmit" @click="submit">
|
|
95
|
+
{{ submitting ? $t("playtest.build_access.sending_report", "Sending…") : $t("playtest.build_access.report_submit", "Send to studio") }}
|
|
96
|
+
</button>
|
|
97
|
+
</div>
|
|
98
|
+
</template>
|
|
99
|
+
|
|
100
|
+
<style scoped lang="scss">
|
|
101
|
+
.report-problem-form {
|
|
102
|
+
&__row {
|
|
103
|
+
margin-bottom: 14px;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
&__label {
|
|
107
|
+
display: block;
|
|
108
|
+
font-size: 12px;
|
|
109
|
+
color: var(--secondary-info-fg, #aaa);
|
|
110
|
+
margin-bottom: 6px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
&__select,
|
|
114
|
+
&__textarea {
|
|
115
|
+
width: 100%;
|
|
116
|
+
background: var(--bg-app-badge, #1a1a1a);
|
|
117
|
+
border: 1px solid var(--button-secondary-default-bg, #2a2a2a);
|
|
118
|
+
color: var(--text-primary, #fff);
|
|
119
|
+
padding: 10px;
|
|
120
|
+
font-size: 13px;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
&__textarea {
|
|
124
|
+
resize: vertical;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
&__uploading,
|
|
128
|
+
&__uploaded {
|
|
129
|
+
display: inline-block;
|
|
130
|
+
margin-left: 8px;
|
|
131
|
+
font-size: 11px;
|
|
132
|
+
color: var(--secondary-info-fg, #888);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
&__uploaded {
|
|
136
|
+
color: var(--primary, #D297FF);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
&__submit {
|
|
140
|
+
margin-top: 4px;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
</style>
|
|
@@ -4,8 +4,11 @@ import {
|
|
|
4
4
|
initiateBuildNda,
|
|
5
5
|
fetchBuildAssets,
|
|
6
6
|
fetchBuildDownloadUrl,
|
|
7
|
+
storeBuildProblemReport,
|
|
8
|
+
uploadPlaytestMedia,
|
|
7
9
|
type PlaytestBuildGrant,
|
|
8
10
|
type PlaytestBuildAsset,
|
|
11
|
+
type StoreBuildProblemReportPayload,
|
|
9
12
|
} from "../services/playtestTesterService";
|
|
10
13
|
import {
|
|
11
14
|
fetchSigningSession,
|
|
@@ -194,6 +197,41 @@ export function usePlaytestBuildAccess(campaignId: number) {
|
|
|
194
197
|
}
|
|
195
198
|
}
|
|
196
199
|
|
|
200
|
+
// ── Problem reports (private, any grant status) ──────────────────────
|
|
201
|
+
const reportSubmitting = ref(false);
|
|
202
|
+
const reportUploading = ref(false);
|
|
203
|
+
const reportSuccess = ref(false);
|
|
204
|
+
const reportError = ref<string | null>(null);
|
|
205
|
+
|
|
206
|
+
/** Never gated on grant status — a tester stuck before NDA sign still needs to reach the studio. */
|
|
207
|
+
async function submitProblemReport(payload: StoreBuildProblemReportPayload) {
|
|
208
|
+
if (!grant.value) return false;
|
|
209
|
+
reportError.value = null;
|
|
210
|
+
reportSubmitting.value = true;
|
|
211
|
+
try {
|
|
212
|
+
await storeBuildProblemReport(grant.value.id, payload);
|
|
213
|
+
reportSuccess.value = true;
|
|
214
|
+
return true;
|
|
215
|
+
} catch (e: any) {
|
|
216
|
+
reportError.value = errorMessage(e, "Could not send your report. Please try again.");
|
|
217
|
+
return false;
|
|
218
|
+
} finally {
|
|
219
|
+
reportSubmitting.value = false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function uploadReportMedia(file: File): Promise<string | null> {
|
|
224
|
+
reportUploading.value = true;
|
|
225
|
+
try {
|
|
226
|
+
const res = await uploadPlaytestMedia(file);
|
|
227
|
+
return (res.data as any)?.data?.url ?? null;
|
|
228
|
+
} catch {
|
|
229
|
+
return null;
|
|
230
|
+
} finally {
|
|
231
|
+
reportUploading.value = false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
197
235
|
return {
|
|
198
236
|
loading,
|
|
199
237
|
submitting,
|
|
@@ -213,6 +251,12 @@ export function usePlaytestBuildAccess(campaignId: number) {
|
|
|
213
251
|
sign,
|
|
214
252
|
getDownloadUrl,
|
|
215
253
|
resetErrors,
|
|
254
|
+
reportSubmitting,
|
|
255
|
+
reportUploading,
|
|
256
|
+
reportSuccess,
|
|
257
|
+
reportError,
|
|
258
|
+
submitProblemReport,
|
|
259
|
+
uploadReportMedia,
|
|
216
260
|
};
|
|
217
261
|
}
|
|
218
262
|
|
package/locales/de.json
CHANGED
|
@@ -24,10 +24,27 @@
|
|
|
24
24
|
"enter_code": "Gib den 6-stelligen Code ein, den wir dir per E-Mail geschickt haben",
|
|
25
25
|
"nda_title": "Unterschreibe die NDA, um die Build freizuschalten",
|
|
26
26
|
"no_builds": "Noch keine Builds verfügbar",
|
|
27
|
+
"report_another": "Weitere Meldung senden",
|
|
28
|
+
"report_attach_screenshot": "Screenshot anhängen (optional)",
|
|
29
|
+
"report_category": "Worum geht es?",
|
|
30
|
+
"report_category_access_problem": "Zugriffs-/Download-Problem",
|
|
31
|
+
"report_category_installation_problem": "Installationsproblem",
|
|
32
|
+
"report_category_other": "Sonstiges",
|
|
33
|
+
"report_category_question": "Frage",
|
|
34
|
+
"report_message": "Nachricht (erforderlich)",
|
|
35
|
+
"report_problem": "Problem melden",
|
|
36
|
+
"report_related_build": "Zugehörige Build (optional)",
|
|
37
|
+
"report_select_build_placeholder": "Allgemeine Frage / nicht build-spezifisch",
|
|
38
|
+
"report_sent": "Gesendet — nur das Studio sieht das.",
|
|
39
|
+
"report_submit": "An Studio senden",
|
|
27
40
|
"resend_code": "Code erneut senden",
|
|
28
|
-
"revoked": "Dein Build
|
|
41
|
+
"revoked": "Dein Zugriff auf diese Build wurde widerrufen.",
|
|
42
|
+
"revoked_contact_link": "Falls du denkst, das ist ein Irrtum, lass es das Studio wissen.",
|
|
43
|
+
"revoked_hint": "Das wird normalerweise vom Studio veranlasst, das die Kampagne betreibt.",
|
|
44
|
+
"select_category_placeholder": "Auswählen…",
|
|
29
45
|
"send_code": "Bestätigungscode senden",
|
|
30
46
|
"sending_code": "Code wird gesendet…",
|
|
47
|
+
"sending_report": "Wird gesendet…",
|
|
31
48
|
"sign": "Unterschreiben",
|
|
32
49
|
"signing": "Wird unterschrieben…",
|
|
33
50
|
"verify": "Bestätigen",
|
package/locales/en.json
CHANGED
|
@@ -24,10 +24,27 @@
|
|
|
24
24
|
"enter_code": "Enter the 6-digit code we emailed you",
|
|
25
25
|
"nda_title": "Sign the NDA to unlock the build",
|
|
26
26
|
"no_builds": "No builds available yet",
|
|
27
|
+
"report_another": "Send another report",
|
|
28
|
+
"report_attach_screenshot": "Attach a screenshot (optional)",
|
|
29
|
+
"report_category": "What's this about?",
|
|
30
|
+
"report_category_access_problem": "Access / download problem",
|
|
31
|
+
"report_category_installation_problem": "Installation problem",
|
|
32
|
+
"report_category_other": "Other",
|
|
33
|
+
"report_category_question": "Question",
|
|
34
|
+
"report_message": "Message (required)",
|
|
35
|
+
"report_problem": "Report a problem",
|
|
36
|
+
"report_related_build": "Related build (optional)",
|
|
37
|
+
"report_select_build_placeholder": "General question / not build-specific",
|
|
38
|
+
"report_sent": "Sent — only the studio can see this.",
|
|
39
|
+
"report_submit": "Send to studio",
|
|
27
40
|
"resend_code": "Resend code",
|
|
28
|
-
"revoked": "Your build
|
|
41
|
+
"revoked": "Your access to this build has been revoked.",
|
|
42
|
+
"revoked_contact_link": "If you think this is a mistake, let them know.",
|
|
43
|
+
"revoked_hint": "This is usually done by the studio running the campaign.",
|
|
44
|
+
"select_category_placeholder": "Select…",
|
|
29
45
|
"send_code": "Send verification code",
|
|
30
46
|
"sending_code": "Sending code…",
|
|
47
|
+
"sending_report": "Sending…",
|
|
31
48
|
"sign": "Sign",
|
|
32
49
|
"signing": "Signing…",
|
|
33
50
|
"verify": "Verify",
|
package/locales/es.json
CHANGED
|
@@ -24,10 +24,27 @@
|
|
|
24
24
|
"enter_code": "Ingresa el código de 6 dígitos que te enviamos por correo",
|
|
25
25
|
"nda_title": "Firma el NDA para desbloquear la build",
|
|
26
26
|
"no_builds": "Todavía no hay builds disponibles",
|
|
27
|
+
"report_another": "Enviar otro reporte",
|
|
28
|
+
"report_attach_screenshot": "Adjuntar una captura de pantalla (opcional)",
|
|
29
|
+
"report_category": "¿De qué se trata?",
|
|
30
|
+
"report_category_access_problem": "Problema de acceso / descarga",
|
|
31
|
+
"report_category_installation_problem": "Problema de instalación",
|
|
32
|
+
"report_category_other": "Otro",
|
|
33
|
+
"report_category_question": "Pregunta",
|
|
34
|
+
"report_message": "Mensaje (obligatorio)",
|
|
35
|
+
"report_problem": "Reportar un problema",
|
|
36
|
+
"report_related_build": "Build relacionada (opcional)",
|
|
37
|
+
"report_select_build_placeholder": "Pregunta general / no específica de una build",
|
|
38
|
+
"report_sent": "Enviado — solo el estudio puede verlo.",
|
|
39
|
+
"report_submit": "Enviar al estudio",
|
|
27
40
|
"resend_code": "Reenviar código",
|
|
28
|
-
"revoked": "Tu acceso a
|
|
41
|
+
"revoked": "Tu acceso a esta build ha sido revocado.",
|
|
42
|
+
"revoked_contact_link": "Si crees que esto es un error, avísale al estudio.",
|
|
43
|
+
"revoked_hint": "Esto normalmente lo hace el estudio a cargo de la campaña.",
|
|
44
|
+
"select_category_placeholder": "Selecciona…",
|
|
29
45
|
"send_code": "Enviar código de verificación",
|
|
30
46
|
"sending_code": "Enviando código…",
|
|
47
|
+
"sending_report": "Enviando…",
|
|
31
48
|
"sign": "Firmar",
|
|
32
49
|
"signing": "Firmando…",
|
|
33
50
|
"verify": "Verificar",
|
package/locales/pt-BR.json
CHANGED
|
@@ -24,10 +24,27 @@
|
|
|
24
24
|
"enter_code": "Digite o código de 6 dígitos que enviamos por e-mail",
|
|
25
25
|
"nda_title": "Assine o NDA para desbloquear a build",
|
|
26
26
|
"no_builds": "Nenhuma build disponível ainda",
|
|
27
|
+
"report_another": "Enviar outro relato",
|
|
28
|
+
"report_attach_screenshot": "Anexar uma captura de tela (opcional)",
|
|
29
|
+
"report_category": "Sobre o que é isso?",
|
|
30
|
+
"report_category_access_problem": "Problema de acesso / download",
|
|
31
|
+
"report_category_installation_problem": "Problema de instalação",
|
|
32
|
+
"report_category_other": "Outro",
|
|
33
|
+
"report_category_question": "Dúvida",
|
|
34
|
+
"report_message": "Mensagem (obrigatória)",
|
|
35
|
+
"report_problem": "Relatar um problema",
|
|
36
|
+
"report_related_build": "Build relacionada (opcional)",
|
|
37
|
+
"report_select_build_placeholder": "Dúvida geral / não específica de uma build",
|
|
38
|
+
"report_sent": "Enviado — só o estúdio vê isso.",
|
|
39
|
+
"report_submit": "Enviar para o estúdio",
|
|
27
40
|
"resend_code": "Reenviar código",
|
|
28
|
-
"revoked": "Seu acesso
|
|
41
|
+
"revoked": "Seu acesso a esta build foi revogado.",
|
|
42
|
+
"revoked_contact_link": "Se você acha que isso é um engano, avise o estúdio.",
|
|
43
|
+
"revoked_hint": "Isso geralmente é feito pelo estúdio responsável pela campanha.",
|
|
44
|
+
"select_category_placeholder": "Selecione…",
|
|
29
45
|
"send_code": "Enviar código de verificação",
|
|
30
46
|
"sending_code": "Enviando código…",
|
|
47
|
+
"sending_report": "Enviando…",
|
|
31
48
|
"sign": "Assinar",
|
|
32
49
|
"signing": "Assinando…",
|
|
33
50
|
"verify": "Verificar",
|
package/locales/ro.json
CHANGED
|
@@ -24,10 +24,27 @@
|
|
|
24
24
|
"enter_code": "Introdu codul din 6 cifre pe care ți l-am trimis pe e-mail",
|
|
25
25
|
"nda_title": "Semnează NDA-ul pentru a debloca build-ul",
|
|
26
26
|
"no_builds": "Niciun build disponibil momentan",
|
|
27
|
+
"report_another": "Trimite un alt raport",
|
|
28
|
+
"report_attach_screenshot": "Atașează o captură de ecran (opțional)",
|
|
29
|
+
"report_category": "Despre ce este vorba?",
|
|
30
|
+
"report_category_access_problem": "Problemă de acces / descărcare",
|
|
31
|
+
"report_category_installation_problem": "Problemă de instalare",
|
|
32
|
+
"report_category_other": "Altceva",
|
|
33
|
+
"report_category_question": "Întrebare",
|
|
34
|
+
"report_message": "Mesaj (obligatoriu)",
|
|
35
|
+
"report_problem": "Raportează o problemă",
|
|
36
|
+
"report_related_build": "Build asociat (opțional)",
|
|
37
|
+
"report_select_build_placeholder": "Întrebare generală / fără legătură cu un build anume",
|
|
38
|
+
"report_sent": "Trimis — doar studioul vede asta.",
|
|
39
|
+
"report_submit": "Trimite studioului",
|
|
27
40
|
"resend_code": "Retrimite codul",
|
|
28
|
-
"revoked": "Accesul tău la build a fost revocat",
|
|
41
|
+
"revoked": "Accesul tău la acest build a fost revocat.",
|
|
42
|
+
"revoked_contact_link": "Dacă crezi că e o greșeală, anunță studioul.",
|
|
43
|
+
"revoked_hint": "Acest lucru este de obicei făcut de studioul care rulează campania.",
|
|
44
|
+
"select_category_placeholder": "Selectează…",
|
|
29
45
|
"send_code": "Trimite codul de verificare",
|
|
30
46
|
"sending_code": "Se trimite codul…",
|
|
47
|
+
"sending_report": "Se trimite…",
|
|
31
48
|
"sign": "Semnează",
|
|
32
49
|
"signing": "Se semnează…",
|
|
33
50
|
"verify": "Verifică",
|
package/package.json
CHANGED
|
@@ -530,6 +530,7 @@ export interface PlaytestBuildAsset {
|
|
|
530
530
|
original_filename: string
|
|
531
531
|
mime_type: string | null
|
|
532
532
|
size_bytes: number | null
|
|
533
|
+
notes: string | null
|
|
533
534
|
status: 'pending' | 'ready' | 'failed'
|
|
534
535
|
}
|
|
535
536
|
|
|
@@ -556,3 +557,40 @@ export const fetchBuildDownloadUrl = (grantId: number, assetId: number) =>
|
|
|
556
557
|
httpService.get<{ data: { url: string; expires_at: string } }>(
|
|
557
558
|
`/playtest/build-access/${grantId}/builds/${assetId}/download`,
|
|
558
559
|
)
|
|
560
|
+
|
|
561
|
+
// ---------------------------------------------------------------------------
|
|
562
|
+
// Build problem reports — a private, one-way tester -> studio channel, never
|
|
563
|
+
// a public wall (this whole feature exists to keep an unreleased build
|
|
564
|
+
// confidential). Works at ANY grant status, including pending_nda — a
|
|
565
|
+
// tester stuck in the sign flow itself (e.g. never got the OTP email) still
|
|
566
|
+
// needs a way to reach the studio.
|
|
567
|
+
// ---------------------------------------------------------------------------
|
|
568
|
+
|
|
569
|
+
export type BuildProblemReportCategory = 'question' | 'installation_problem' | 'access_problem' | 'other'
|
|
570
|
+
|
|
571
|
+
export interface StoreBuildProblemReportPayload {
|
|
572
|
+
category: BuildProblemReportCategory
|
|
573
|
+
message: string
|
|
574
|
+
playtest_build_asset_id?: number
|
|
575
|
+
media_url?: string
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
export interface PlaytestBuildProblemReport {
|
|
579
|
+
id: number
|
|
580
|
+
playtest_campaign_id: number
|
|
581
|
+
playtest_build_grant_id: number
|
|
582
|
+
playtest_build_asset_id: number | null
|
|
583
|
+
category: BuildProblemReportCategory
|
|
584
|
+
message: string
|
|
585
|
+
media_url: string | null
|
|
586
|
+
status: 'open' | 'in_progress' | 'resolved'
|
|
587
|
+
studio_response: string | null
|
|
588
|
+
responded_at: string | null
|
|
589
|
+
created_at: string
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
export const storeBuildProblemReport = (grantId: number, payload: StoreBuildProblemReportPayload) =>
|
|
593
|
+
httpService.post<{ data: PlaytestBuildProblemReport }>(
|
|
594
|
+
`/playtest/build-access/${grantId}/problem-reports`,
|
|
595
|
+
payload,
|
|
596
|
+
)
|