diff --git a/admin/src/PlaytestDetailPage.test.tsx b/admin/src/PlaytestDetailPage.test.tsx index 8189421..ef4baf1 100644 --- a/admin/src/PlaytestDetailPage.test.tsx +++ b/admin/src/PlaytestDetailPage.test.tsx @@ -183,6 +183,22 @@ describe('PlaytestDetailPage shell', () => { await waitFor(() => expect(screen.getByTestId('distribution-tab')).toBeInTheDocument()) }) + it('Survey tab shows the notify notice with a Learn more / Show less toggle', async () => { + renderDetail('autumn-draft') + const user = userEvent.setup() + await user.click(screen.getByRole('tab', { name: 'Survey' })) + const notice = await screen.findByTestId('survey-notify-notice') + // Compact line always shown; the detail (bullets + caveat) is hidden until "Learn more". + expect(within(notice).getByText(/your approved testers are notified automatically/i)).toBeInTheDocument() + expect(within(notice).queryByText(/won't re-send any DMs/i)).not.toBeInTheDocument() + + await user.click(within(notice).getByText(/learn more/i)) + expect(within(notice).getByText(/won't re-send any DMs/i)).toBeInTheDocument() + + await user.click(within(notice).getByText(/show less/i)) + expect(within(notice).queryByText(/won't re-send any DMs/i)).not.toBeInTheDocument() + }) + it('Info tab renders the read-only field grid + Edit button', () => { renderDetail('autumn-draft') expect(screen.getByTestId('playtest-info-tab')).toBeInTheDocument() diff --git a/admin/src/tabs/SurveyTab.tsx b/admin/src/tabs/SurveyTab.tsx index 78474ef..500a325 100644 --- a/admin/src/tabs/SurveyTab.tsx +++ b/admin/src/tabs/SurveyTab.tsx @@ -1,32 +1,20 @@ import { useAppUIContext } from '@accelbyte/sdk-extend-app-ui' +import { InfoCircleFilled } from '@ant-design/icons' import { useQueryClient } from '@tanstack/react-query' -import { - Alert, - Button, - Card, - Checkbox, - Form, - Input, - Popconfirm, - Select, - Space, - Spin, - Typography, - message -} from 'antd' +import { Alert, Button, Card, Checkbox, Form, Input, Popconfirm, Select, Space, Spin, Typography, message } from 'antd' import { useState } from 'react' -import type { V1MultiChoiceOption } from '../playtesthubapi/generated-definitions/V1MultiChoiceOption' -import type { V1Playtest } from '../playtesthubapi/generated-definitions/V1Playtest' -import type { V1Survey } from '../playtesthubapi/generated-definitions/V1Survey' -import type { V1SurveyQuestion } from '../playtesthubapi/generated-definitions/V1SurveyQuestion' import { Key_PlaytesthubServiceAdmin, usePlaytesthubServiceAdminApi_CreateSurvey_ByPlaytestIdMutation, usePlaytesthubServiceAdminApi_PatchSurvey_ByPlaytestIdMutation } from '../playtesthubapi/generated-admin/queries/PlaytesthubServiceAdmin.query' +import type { V1MultiChoiceOption } from '../playtesthubapi/generated-definitions/V1MultiChoiceOption' +import type { V1Playtest } from '../playtesthubapi/generated-definitions/V1Playtest' +import type { V1Survey } from '../playtesthubapi/generated-definitions/V1Survey' +import type { V1SurveyQuestion } from '../playtesthubapi/generated-definitions/V1SurveyQuestion' import { usePlaytesthubServiceApi_GetSurveyPlayer_ByPlaytestId } from '../playtesthubapi/generated-public/queries/PlaytesthubService.query' -import { PlaytestStatus } from '../shared/playtesthub-enums' import { toastError } from '../shared/api-error' +import { PlaytestStatus } from '../shared/playtesthub-enums' const QUESTION_TYPE_TEXT = 'SURVEY_QUESTION_TYPE_TEXT' const QUESTION_TYPE_RATING = 'SURVEY_QUESTION_TYPE_RATING' @@ -117,11 +105,7 @@ export function SurveyTab({ playtest }: { playtest: V1Playtest }) { // Player GetSurvey is the authoritative read path (no admin GET in proto). // Returns NotFound for DRAFT playtests — render the warning + blank form in // that case so first-version edits still work. - const surveyQuery = usePlaytesthubServiceApi_GetSurveyPlayer_ByPlaytestId( - sdk, - { playtestId }, - { enabled: hasSurvey, retry: false } - ) + const surveyQuery = usePlaytesthubServiceApi_GetSurveyPlayer_ByPlaytestId(sdk, { playtestId }, { enabled: hasSurvey, retry: false }) if (hasSurvey && surveyQuery.isLoading) return @@ -156,6 +140,7 @@ function SurveyTabForm({ playtestId, initialSurvey, hasSurvey, draftPreloadFaile if (initialSurvey?.questions?.length) return initialSurvey.questions.map(questionToDraft) return [freshTextQuestion()] }) + const [showNotifyDetail, setShowNotifyDetail] = useState(false) const version = initialSurvey?.version ?? null const createMutation = usePlaytesthubServiceAdminApi_CreateSurvey_ByPlaytestIdMutation(sdk, { @@ -239,116 +224,165 @@ function SurveyTabForm({ playtestId, initialSurvey, hasSurvey, draftPreloadFaile const saving = createMutation.isPending || editMutation.isPending return ( - - {hasSurvey ? 'Save new version' : 'Create survey'} - - } - > - - - {hasSurvey ? 'Editing existing survey.' : 'Configure the post-playtest survey for approved players.'} - {version != null && ` Current version v${version} — saving creates v${version + 1}.`} - + <> + } + style={{ marginBottom: 16, alignItems: 'flex-start' }} + data-testid="survey-notify-notice" + message={ +
+ When you create this survey, your approved testers are notified automatically. + {!showNotifyDetail && ( + <> + {' '} + setShowNotifyDetail(true)}>Learn more + + )} + {showNotifyDetail && ( + <> +
    +
  • + Already approved (NDA accepted, if required): a one-time DM with the link. +
  • +
  • + Approved later: the link is added to their approval DM automatically. +
  • +
  • + Anytime: it also appears on the Playtest sign-up page (shown after the player + signs up). +
  • +
+ + Editing the survey afterward won't re-send any DMs. + + setShowNotifyDetail(false)} style={{ display: 'inline-block', marginTop: 8 }}> + Show less + + + )} +
+ } + /> + + {hasSurvey ? 'Save new version' : 'Create survey'} + + }> + + + {hasSurvey ? 'Editing existing survey.' : 'Configure the post-playtest survey for approved players.'} + {version != null && ` Current version v${version} — saving creates v${version + 1}.`} + - {draftPreloadFailed && ( - - )} + {draftPreloadFailed && ( + + )} - - {questions.map((q, i) => ( -
-
- Question {i + 1} - - - - removeQuestion(q.key)}> - - - + + removeQuestion(q.key)}> + + + +
+
+ + updateOption(q.key, oIdx, e.target.value)} + placeholder={`Option ${oIdx + 1}`} + /> + + + ))} + + + + + )} +
-
- - updateOption(q.key, oIdx, e.target.value)} - placeholder={`Option ${oIdx + 1}`} - /> - - - ))} - - - - - )} -
- - ))} - + ))} + +
-
-
+ + ) }