@autobusal/common 1.18.0 → 1.20.0
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/CHANGELOG.md +18 -0
- package/Rating/Rating.tsx +73 -0
- package/Rating/styles.ts +31 -0
- package/Table/Actions/Get.tsx +8 -1
- package/index.ts +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,24 @@ All notable changes to `@autobusal/common` are documented here. This project fol
|
|
|
6
6
|
> Note: 1.10.0 through 1.15.1 were published without changelog entries. The
|
|
7
7
|
> gap is left as-is rather than reconstructed after the fact.
|
|
8
8
|
|
|
9
|
+
## 1.20.0
|
|
10
|
+
|
|
11
|
+
`Rating` — stars, average and count, or nothing at all. Rendering nothing is
|
|
12
|
+
the feature: obtapi withholds a score until there are enough published reviews
|
|
13
|
+
to say something honest, so an absent rating has to read as "not saying yet"
|
|
14
|
+
rather than as a zero. Anything filling the gap with empty stars would turn a
|
|
15
|
+
new operator into a badly-rated one. Half stars are drawn, because rounding 3.5
|
|
16
|
+
down loses the difference between middling and good — precisely the range where
|
|
17
|
+
a reader is still deciding.
|
|
18
|
+
|
|
19
|
+
## 1.19.0
|
|
20
|
+
|
|
21
|
+
`extra` joins `create` and `search` as a top-of-table slot rather than a
|
|
22
|
+
per-row action. Any table opting into the extra slot also rendered one empty,
|
|
23
|
+
iconless, still-clickable button in every row's options cell — Icon has no
|
|
24
|
+
case for `extra`, so it drew nothing while taking the click. The refund queue
|
|
25
|
+
has been doing this all along; the new review queue would have too.
|
|
26
|
+
|
|
9
27
|
## [1.18.0] - 2026-08-02
|
|
10
28
|
|
|
11
29
|
### Added
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import { AiFillStar, AiOutlineStar } from 'react-icons/ai';
|
|
3
|
+
import { BsStarHalf } from 'react-icons/bs';
|
|
4
|
+
import { Container, Stars, Average, Count } from './styles';
|
|
5
|
+
import { RatingData } from '@autobusal/providers/types/reviews';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
// an aggregate: stars, the average and how many reviews built it
|
|
9
|
+
rating?: RatingData | null
|
|
10
|
+
// Edited: Ferjolt Ozuni - Date: 2026-08-03
|
|
11
|
+
// ONE review's own rating: stars alone. Passing it through `rating` with a
|
|
12
|
+
// count of 1 drew "5.0 (1)" beside every single review and a tooltip
|
|
13
|
+
// reading "from 1 reviews" - a summary of a set of one, which is not what
|
|
14
|
+
// an individual review is.
|
|
15
|
+
value?: number
|
|
16
|
+
size?: 'small' | 'normal'
|
|
17
|
+
t: TFunction<'common'>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A published rating.
|
|
22
|
+
*
|
|
23
|
+
* Edited: Ferjolt Ozuni - Date: 2026-08-03
|
|
24
|
+
*
|
|
25
|
+
* Renders NOTHING when there is no rating, and that is the important part:
|
|
26
|
+
* obtapi withholds the score until a route or operator has enough published
|
|
27
|
+
* reviews to say something honest, so an absent rating must read as "we are
|
|
28
|
+
* not saying yet" rather than as a zero. Anything that filled the gap with
|
|
29
|
+
* empty stars or "0.0" would turn a new operator into a badly-rated one.
|
|
30
|
+
*
|
|
31
|
+
* The half star matters more than it looks. Rounding 3.5 down to three full
|
|
32
|
+
* stars loses the difference between a middling operator and a good one,
|
|
33
|
+
* which is precisely the range where a reader is still deciding.
|
|
34
|
+
*/
|
|
35
|
+
const Rating = ({ rating, value, size, t }: Props): (JSX.Element | null) => {
|
|
36
|
+
const average = value ?? rating?.average;
|
|
37
|
+
|
|
38
|
+
if (average === undefined || (value === undefined && !rating?.count)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const rounded = Math.round(average * 2) / 2;
|
|
43
|
+
|
|
44
|
+
const title = value !== undefined
|
|
45
|
+
? t('rating.value', { average })
|
|
46
|
+
: t('rating.summary', { average, count: rating?.count });
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Container $size={ size ?? 'normal' } title={ title }>
|
|
50
|
+
<Stars aria-hidden="true">
|
|
51
|
+
{ [ 1, 2, 3, 4, 5 ].map(value => {
|
|
52
|
+
if (rounded >= value) {
|
|
53
|
+
return <AiFillStar key={ value } />;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return rounded >= value - 0.5
|
|
57
|
+
? <BsStarHalf key={ value } />
|
|
58
|
+
: <AiOutlineStar key={ value } />;
|
|
59
|
+
}) }
|
|
60
|
+
</Stars>
|
|
61
|
+
|
|
62
|
+
{ rating && (
|
|
63
|
+
<>
|
|
64
|
+
<Average>{ rating.average.toFixed(1) }</Average>
|
|
65
|
+
|
|
66
|
+
<Count>({ rating.count })</Count>
|
|
67
|
+
</>
|
|
68
|
+
) }
|
|
69
|
+
</Container>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default Rating;
|
package/Rating/styles.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const Container = styled.span<{ $size: 'small' | 'normal' }>`
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
gap: 5px;
|
|
7
|
+
white-space: nowrap;
|
|
8
|
+
line-height: 1;
|
|
9
|
+
|
|
10
|
+
${ props => props.$size === 'small' && css`
|
|
11
|
+
font-size: ${ props => props.theme.size.s };
|
|
12
|
+
` }
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
export const Stars = styled.span`
|
|
16
|
+
display: inline-flex;
|
|
17
|
+
gap: 1px;
|
|
18
|
+
color: ${ props => props.theme.primary.normal };
|
|
19
|
+
|
|
20
|
+
svg {
|
|
21
|
+
display: block;
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
export const Average = styled.strong`
|
|
26
|
+
font-weight: 700;
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
export const Count = styled.span`
|
|
30
|
+
color: ${ props => props.theme.font.faded };
|
|
31
|
+
`;
|
package/Table/Actions/Get.tsx
CHANGED
|
@@ -14,7 +14,14 @@ interface Props {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const Get = ({ id, url, urlWith, type, t, handlers, navigate }: Props): (JSX.Element | null) => {
|
|
17
|
-
|
|
17
|
+
// Edited: Ferjolt Ozuni - Date: 2026-08-02
|
|
18
|
+
// 'extra' belongs with 'create' and 'search': all three are TOP-of-table
|
|
19
|
+
// slots, not per-row actions. Without it here, any table that opts into
|
|
20
|
+
// the extra slot (the refund queue does, the review queue now does too)
|
|
21
|
+
// also renders one empty, iconless, clickable button in every row's
|
|
22
|
+
// options cell - Icon has no case for 'extra', so it draws nothing at all
|
|
23
|
+
// while still taking the click.
|
|
24
|
+
if (['create', 'search', 'extra'].includes(type)) {
|
|
18
25
|
return null;
|
|
19
26
|
}
|
|
20
27
|
|
package/index.ts
CHANGED
|
@@ -25,6 +25,7 @@ import Pagination from './Pagination/Pagination';
|
|
|
25
25
|
import Paragraph from './Loading/Paragraph';
|
|
26
26
|
import Passengers from './Passengers/Passengers';
|
|
27
27
|
import Password from './Password/Password';
|
|
28
|
+
import Rating from './Rating/Rating';
|
|
28
29
|
import Policy from './Policy/Policy';
|
|
29
30
|
import Report from './Report/Report';
|
|
30
31
|
import RouteFeature from './RouteFeature/RouteFeature';
|
|
@@ -73,6 +74,7 @@ export {
|
|
|
73
74
|
Paragraph,
|
|
74
75
|
Passengers,
|
|
75
76
|
Password,
|
|
77
|
+
Rating,
|
|
76
78
|
Policy,
|
|
77
79
|
Report,
|
|
78
80
|
RouteFeature,
|