Skip to content

Commit

Permalink
(PC-32365)[API][PRO] feat: update pro user reviews
Browse files Browse the repository at this point in the history
  • Loading branch information
asaez-pass authored and lmaubert-pass committed Nov 5, 2024
1 parent 6719095 commit c1c2fbf
Show file tree
Hide file tree
Showing 20 changed files with 148 additions and 136 deletions.
13 changes: 6 additions & 7 deletions api/src/pcapi/routes/pro/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,20 +340,19 @@ def connect_as(token: str) -> Response:
return flask.redirect(token_data.redirect_link, code=302)


@blueprint.pro_private_api.route("/users/log-new-nav-review", methods=["POST"])
@blueprint.pro_private_api.route("/users/log-user-review", methods=["POST"])
@login_required
@spectree_serialize(on_success_status=204, api=blueprint.pro_private_schema)
def submit_new_nav_review(body: users_serializers.SubmitReviewRequestModel) -> None:
def submit_user_review(body: users_serializers.SubmitReviewRequestModel) -> None:
check_user_has_access_to_offerer(current_user, body.offererId)

logger.info(
"User with new nav activated submitting review",
"User submitting review",
extra={
"offerer_id": body.offererId,
"isConvenient": body.isConvenient,
"isPleasant": body.isPleasant,
"comment": body.comment,
"user_satisfaction": body.userSatisfaction,
"user_comment": body.userComment,
"source_page": body.location,
},
technical_message_id="new_nav_review",
technical_message_id="user_review",
)
5 changes: 2 additions & 3 deletions api/src/pcapi/routes/serialization/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,8 @@ class ProFlagsQueryModel(BaseModel):


class SubmitReviewRequestModel(BaseModel):
isPleasant: bool
isConvenient: bool
comment: str
userSatisfaction: str
userComment: str
offererId: int
location: str

Expand Down
27 changes: 12 additions & 15 deletions api/tests/routes/pro/post_user_review_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,30 @@

class PostUserReviewTest:

def test_user_can_successfully_submit_side_nav_review(self, client, caplog):
def test_user_can_successfully_submit_review(self, client, caplog):
user = users_factories.ProFactory()
offerer = offerers_factories.OffererFactory()
offerers_factories.UserOffererFactory(user=user, offerer=offerer)

expected_data = {
"isConvenient": True,
"isPleasant": False,
"comment": "c'est quand même très blanc",
"userSatisfaction": "Bonne",
"userComment": "c'est quand même très blanc",
"offererId": offerer.id,
"location": f"/offerers/{id}",
}

client = client.with_session_auth(user.email)

with caplog.at_level(logging.INFO):
response = client.post("/users/log-new-nav-review", json=expected_data)
response = client.post("/users/log-user-review", json=expected_data)

assert response.status_code == 204
assert "User with new nav activated submitting review" in caplog.messages
assert "User submitting review" in caplog.messages
assert caplog.records[0].extra["offerer_id"] == offerer.id
assert caplog.records[0].extra["isConvenient"] == expected_data["isConvenient"]
assert caplog.records[0].extra["isPleasant"] == expected_data["isPleasant"]
assert caplog.records[0].extra["comment"] == expected_data["comment"]
assert caplog.records[0].extra["user_satisfaction"] == expected_data["userSatisfaction"]
assert caplog.records[0].extra["user_comment"] == expected_data["userComment"]
assert caplog.records[0].extra["source_page"] == f"/offerers/{id}"
assert caplog.records[0].technical_message_id == "new_nav_review"
assert caplog.records[0].technical_message_id == "user_review"

def test_user_cannot_submit_review_for_foreign_offerer(self, client, caplog):
user = users_factories.ProFactory()
Expand All @@ -45,17 +43,16 @@ def test_user_cannot_submit_review_for_foreign_offerer(self, client, caplog):
foreign_offerer = offerers_factories.OffererFactory()

data = {
"isConvenient": False,
"isPleasant": False,
"comment": "messing with statistics again è_é",
"userSatisfaction": "Mauvaise",
"userComment": "messing with statistics again è_é",
"offererId": foreign_offerer.id,
"location": f"/offerers/{id}/",
}

client = client.with_session_auth(user.email)

with caplog.at_level(logging.INFO):
response = client.post("/users/log-new-nav-review", json=data)
response = client.post("/users/log-user-review", json=data)

assert response.status_code == 403
assert "User with new nav activated submitting review" not in caplog.messages
assert "User submitting review" not in caplog.messages
5 changes: 2 additions & 3 deletions pro/src/apiClient/v1/models/SubmitReviewRequestModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
/* tslint:disable */
/* eslint-disable */
export type SubmitReviewRequestModel = {
comment: string;
isConvenient: boolean;
isPleasant: boolean;
location: string;
offererId: number;
userComment: string;
userSatisfaction: string;
};

6 changes: 3 additions & 3 deletions pro/src/apiClient/v1/services/DefaultService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2319,17 +2319,17 @@ export class DefaultService {
});
}
/**
* submit_new_nav_review <POST>
* submit_user_review <POST>
* @param requestBody
* @returns void
* @throws ApiError
*/
public submitNewNavReview(
public submitUserReview(
requestBody?: SubmitReviewRequestModel,
): CancelablePromise<void> {
return this.httpRequest.request({
method: 'POST',
url: '/users/log-new-nav-review',
url: '/users/log-user-review',
body: requestBody,
mediaType: 'application/json',
errors: {
Expand Down
6 changes: 3 additions & 3 deletions pro/src/app/App/layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { useMediaQuery } from 'commons/hooks/useMediaQuery'
import { selectCurrentUser } from 'commons/store/user/selectors'
import { Footer } from 'components/Footer/Footer'
import { Header } from 'components/Header/Header'
import { NewNavReview } from 'components/NewNavReview/NewNavReview'
import { SkipLinks } from 'components/SkipLinks/SkipLinks'
import { UserReview } from 'components/UserReview/UserReview'
import fullGoTop from 'icons/full-go-top.svg'
import fullInfoIcon from 'icons/full-info.svg'
import { SvgIcon } from 'ui-kit/SvgIcon/SvgIcon'
Expand Down Expand Up @@ -39,7 +39,7 @@ export const Layout = ({

const isMobileScreen = useMediaQuery('(max-width: 46.5rem)')

const shouldDisplayNewNavReview =
const shouldDisplayUserReview =
layout !== 'funnel' && layout !== 'without-nav'

return (
Expand Down Expand Up @@ -97,7 +97,7 @@ export const Layout = ({
/>
)}
<div id="content-wrapper" className={styles['content-wrapper']}>
{shouldDisplayNewNavReview && <NewNavReview />}
{shouldDisplayUserReview && <UserReview />}
<div
className={cn(styles['content-container'], {
[styles['content-container-funnel']]: layout === 'funnel',
Expand Down
19 changes: 19 additions & 0 deletions pro/src/commons/utils/__specs__/timezone.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
import {
isValidTime,
convertTimeFromVenueTimezoneToUtc,
formatLocalTimeDateString,
getDepartmentTimezone,
} from '../timezone'

describe('isValidTime', () => {
it('should return true for a valid time', () => {
const time = '12:00'
const toBePaddedTime = '9:00'
expect(isValidTime(time)).toBe(true)
expect(isValidTime(toBePaddedTime)).toBe(true)
})

it('should return false for an invalid time', () => {
const undefinedTime = undefined
const randomStringTime = ''
const outOfBoundTime = '24:00'
expect(isValidTime(undefinedTime)).toBe(false)
expect(isValidTime(randomStringTime)).toBe(false)
expect(isValidTime(outOfBoundTime)).toBe(false)
})
})

describe('formatLocalTimeDateString', () => {
it('should return a formatted date with Paris departement code and specified format', () => {
const date = '2019-02-28T21:59:00Z'
Expand Down
4 changes: 4 additions & 0 deletions pro/src/commons/utils/timezone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import {

import { getToday } from './date'

const timeRegex = /^([01]?[0-9]|2[0-3]):([0-5][0-9])$/
export const isValidTime = (time?: string | null): boolean =>
Boolean(time && timeRegex.test(time))

export const formatLocalTimeDateString = (
dateIsoString: string | number | Date,
departementCode?: string | null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { usePaginationWithSearchParams } from 'commons/hooks/usePagination'
import { getToday } from 'commons/utils/date'
import { hasErrorCode } from 'commons/utils/error'
import {
isValidTime,
convertTimeFromVenueTimezoneToUtc,
getLocalDepartementDateTimeFromUtc,
} from 'commons/utils/timezone'
Expand Down Expand Up @@ -155,7 +156,9 @@ export const StocksEventEdition = ({
const [isRecurrenceModalOpen, setIsRecurrenceModalOpen] = useState(false)
const [stocksCount, setStocksCount] = useState<number | null>(null)
const [dateFilter, setDateFilter] = useState(searchParams.get('date'))
const [timeFilter, setTimeFilter] = useState(searchParams.get('time'))
const [timeFilter, setTimeFilter] = useState<string>(
searchParams.get('time') || ''
)
const [priceCategoryIdFilter, setPriceCategoryIdFilter] = useState(
searchParams.get('priceCategoryId')
)
Expand All @@ -180,7 +183,7 @@ export const StocksEventEdition = ({
api.getStocks(
offer.id,
dateFilter ? dateFilter : undefined,
timeFilter
isValidTime(timeFilter)
? convertTimeFromVenueTimezoneToUtc(timeFilter, departmentCode)
: undefined,
priceCategoryIdFilter ? Number(priceCategoryIdFilter) : undefined,
Expand Down Expand Up @@ -529,7 +532,7 @@ export const StocksEventEdition = ({
setTimeFilter(event.target.value)
onFilterChange()
}}
value={timeFilter ?? ''}
value={timeFilter}
filterVariant
aria-label="Filtrer par horaire"
/>
Expand Down

This file was deleted.

9 changes: 6 additions & 3 deletions pro/src/components/StocksEventList/StocksEventList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { selectCurrentOffererId } from 'commons/store/user/selectors'
import { formatPrice } from 'commons/utils/formatPrice'
import { pluralize, pluralizeString } from 'commons/utils/pluralize'
import {
isValidTime,
convertTimeFromVenueTimezoneToUtc,
formatLocalTimeDateString,
} from 'commons/utils/timezone'
Expand Down Expand Up @@ -100,7 +101,9 @@ export const StocksEventList = ({
useState<number>(0)
const [isDeleteAllLoading, setIsDeleteAllLoading] = useState(false)
const [dateFilter, setDateFilter] = useState(searchParams.get('date'))
const [timeFilter, setTimeFilter] = useState(searchParams.get('time'))
const [timeFilter, setTimeFilter] = useState<string>(
searchParams.get('time') || ''
)
const [priceCategoryIdFilter, setPriceCategoryIdFilter] = useState(
searchParams.get('priceCategoryId')
)
Expand All @@ -114,7 +117,7 @@ export const StocksEventList = ({
api.getStocks(
offer.id,
dateFilter ? dateFilter : undefined,
timeFilter
isValidTime(timeFilter)
? convertTimeFromVenueTimezoneToUtc(timeFilter, departmentCode)
: undefined,
priceCategoryIdFilter ? Number(priceCategoryIdFilter) : undefined,
Expand Down Expand Up @@ -427,7 +430,7 @@ export const StocksEventList = ({
setTimeFilter(event.target.value)
onFilterChange()
}}
value={timeFilter ?? ''}
value={timeFilter}
filterVariant
aria-label="Filtrer par horaire"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
@use "styles/mixins/_fonts.scss" as fonts;
@use "styles/variables/_size.scss" as size;

.new-nav-review {
.user-review {
&-container {
display: flex;
gap: rem.torem(8px);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,31 @@ import { ButtonVariant } from 'ui-kit/Button/types'
import { DialogBuilder } from 'ui-kit/DialogBuilder/DialogBuilder'
import { SvgIcon } from 'ui-kit/SvgIcon/SvgIcon'

import styles from './NewNavReview.module.scss'
import { NewNavReviewDialog } from './NewNavReviewDialog/NewNavReviewDialog'
import styles from './UserReview.module.scss'
import { UserReviewDialog } from './UserReviewDialog/UserReviewDialog'

export const NewNavReview = () => {
export const UserReview = () => {
return (
<>
<div className={styles['new-nav-review-container']}>
<div className={styles['user-review-container']}>
<SvgIcon
className={styles['new-nav-review-icon']}
className={styles['user-review-icon']}
src={strokeShoutIcon}
alt=""
width="20"
/>
<div>
<span className={styles['new-nav-review-bold-text']}>
Vous êtes sur la nouvelle interface !
<span className={styles['user-review-bold-text']}>
Que pensez-vous du pass Culture Pro ?
</span>{' '}
Dites-nous ce que vous en pensez pour nous aider à l’améliorer.
Aidez-nous à l’améliorer.
</div>
<DialogBuilder
trigger={
<Button variant={ButtonVariant.SECONDARY}>Je donne mon avis</Button>
}
>
<NewNavReviewDialog />
<UserReviewDialog />
</DialogBuilder>
</div>
</>
Expand Down
Loading

0 comments on commit c1c2fbf

Please sign in to comment.