richie-education 2.25.0-b2.dev83 → 2.25.0-b2.dev91

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.
Files changed (25) hide show
  1. package/js/components/CourseGlimpseList/index.spec.tsx +1 -1
  2. package/js/components/CourseGlimpseList/index.tsx +1 -1
  3. package/js/pages/TeacherDashboardContractsLayout/components/ContractFiltersBar/index.tsx +10 -49
  4. package/js/utils/OrderHelper/index.ts +32 -0
  5. package/js/widgets/Dashboard/components/DashboardAvatar/_styles.scss +4 -3
  6. package/js/widgets/Dashboard/components/DashboardAvatar/index.tsx +1 -1
  7. package/js/widgets/Dashboard/components/DashboardItem/CourseEnrolling/index.tsx +5 -2
  8. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.tsx +2 -2
  9. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.tsx +9 -6
  10. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateMessage/index.spec.tsx +2 -5
  11. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateMessage/index.tsx +5 -5
  12. package/js/widgets/Dashboard/components/DashboardListAvatar/_styles.scss +8 -0
  13. package/js/widgets/Dashboard/components/DashboardListAvatar/index.tsx +11 -0
  14. package/js/widgets/Dashboard/components/DashboardOrderLoader/index.tsx +5 -2
  15. package/js/widgets/Dashboard/components/DashboardSidebar/_styles.scss +2 -0
  16. package/js/widgets/Dashboard/components/FilterOrganization/index.tsx +58 -0
  17. package/js/widgets/Dashboard/components/FiltersBar/index.tsx +9 -0
  18. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCourseRuns/EnrollableCourseRunList.tsx +4 -2
  19. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.tsx +4 -2
  20. package/package.json +1 -1
  21. package/scss/components/_index.scss +1 -0
  22. package/scss/objects/_course_glimpses.scss +0 -7
  23. package/scss/objects/_index.scss +1 -0
  24. package/scss/objects/_list.scss +8 -0
  25. package/js/widgets/Dashboard/components/DashboardItem/utils/order.ts +0 -15
@@ -59,7 +59,7 @@ describe('widgets/Search/components/CourseGlimpseList', () => {
59
59
  expect(srOnlyCount).toHaveAttribute('aria-live', 'polite');
60
60
  expect(srOnlyCount).toHaveAttribute('aria-atomic', 'true');
61
61
  // the message shown in the UI
62
- expect(container.querySelector('.course-glimpse-list__count')).toHaveAttribute(
62
+ expect(container.querySelector('.list__count-description')).toHaveAttribute(
63
63
  'aria-hidden',
64
64
  'true',
65
65
  );
@@ -61,7 +61,7 @@ export const CourseGlimpseList = ({
61
61
  }}
62
62
  />
63
63
  </div>
64
- <div className="course-glimpse-list__count" aria-hidden="true">
64
+ <div className="course-glimpse-list__count list__count-description" aria-hidden="true">
65
65
  <FormattedMessage
66
66
  {...messages.courseCount}
67
67
  values={{
@@ -1,10 +1,9 @@
1
1
  import { Select, SelectProps } from '@openfun/cunningham-react';
2
2
  import { defineMessages, useIntl } from 'react-intl';
3
- import { useEffect } from 'react';
4
- import { useOrganizations } from 'hooks/useOrganizations';
3
+ import FiltersBar from 'widgets/Dashboard/components/FiltersBar';
5
4
  import { ContractState } from 'types/Joanie';
6
5
  import { ContractHelper, ContractStatePoV } from 'utils/ContractHelper';
7
- import { Spinner } from 'components/Spinner';
6
+ import FilterOrganization from 'widgets/Dashboard/components/FilterOrganization';
8
7
 
9
8
  export const messages = defineMessages({
10
9
  organizationFilterLabel: {
@@ -31,11 +30,6 @@ interface ContractFiltersBarProps {
31
30
  hideFilterSignatureState?: boolean;
32
31
  }
33
32
 
34
- interface FilterProps {
35
- defaultValue?: SelectProps['defaultValue'];
36
- onChange: (value: Partial<ContractListFilters>) => void;
37
- }
38
-
39
33
  const ContractFiltersBar = ({
40
34
  defaultValues,
41
35
  onFiltersChange,
@@ -43,9 +37,9 @@ const ContractFiltersBar = ({
43
37
  hideFilterSignatureState = false,
44
38
  }: ContractFiltersBarProps) => {
45
39
  return (
46
- <div className="dashboard__page__actions-row dashboard__page__actions-row--end">
40
+ <FiltersBar>
47
41
  {!hideFilterOrganization && (
48
- <OrganizationContractFilter
42
+ <FilterOrganization
49
43
  defaultValue={defaultValues?.organization_id}
50
44
  onChange={onFiltersChange}
51
45
  />
@@ -56,49 +50,16 @@ const ContractFiltersBar = ({
56
50
  onChange={onFiltersChange}
57
51
  />
58
52
  )}
59
- </div>
53
+ </FiltersBar>
60
54
  );
61
55
  };
62
56
 
63
- const OrganizationContractFilter = ({ defaultValue, onChange }: FilterProps) => {
64
- const intl = useIntl();
65
- const {
66
- items: organizations,
67
- states: { isFetched },
68
- } = useOrganizations();
69
-
70
- const organizationOptions = organizations.map((organization) => ({
71
- label: organization.title,
72
- value: organization.id,
73
- }));
74
-
75
- const handleChange: SelectProps['onChange'] = (e) => {
76
- const value = e.target.value as string;
77
- onChange({ organization_id: value });
78
- };
79
-
80
- useEffect(() => {
81
- if (isFetched && defaultValue === undefined) {
82
- onChange({ organization_id: organizationOptions[0]?.value });
83
- }
84
- }, [defaultValue, isFetched]);
85
-
86
- if (!isFetched) return <Spinner />;
87
-
88
- return (
89
- <Select
90
- label={intl.formatMessage(messages.organizationFilterLabel)}
91
- options={organizationOptions}
92
- defaultValue={defaultValue || organizationOptions[0].value}
93
- onChange={handleChange}
94
- disabled={!isFetched}
95
- clearable={false}
96
- searchable={true}
97
- />
98
- );
99
- };
57
+ interface ContractFilterProps {
58
+ defaultValue?: SelectProps['defaultValue'];
59
+ onChange: (value: Partial<ContractListFilters>) => void;
60
+ }
100
61
 
101
- const SignatureStateFilter = ({ defaultValue, onChange }: FilterProps) => {
62
+ const SignatureStateFilter = ({ defaultValue, onChange }: ContractFilterProps) => {
102
63
  const intl = useIntl();
103
64
  const contractStateOptions = Object.values(ContractState)
104
65
  .filter((value) => value !== ContractState.UNSIGNED)
@@ -0,0 +1,32 @@
1
+ import {
2
+ OrderEnrollment,
3
+ ACTIVE_ORDER_STATES,
4
+ Order,
5
+ OrderState,
6
+ ContractDefinition,
7
+ } from 'types/Joanie';
8
+
9
+ /**
10
+ * Helper class for orders
11
+ */
12
+ export class OrderHelper {
13
+ /**
14
+ * return an Order from the given list that match given productId
15
+ */
16
+ static getActiveEnrollmentOrder(orders: OrderEnrollment[], productId: string) {
17
+ const filter = (order: OrderEnrollment) =>
18
+ ACTIVE_ORDER_STATES.includes(order.state) && order.product_id === productId;
19
+ return orders.find(filter);
20
+ }
21
+
22
+ /**
23
+ * tell us if a order need to be sign by it's owner (the learner user).
24
+ */
25
+ static orderNeedsSignature(order: Order, contractDefinition?: ContractDefinition) {
26
+ return (
27
+ order?.state === OrderState.VALIDATED &&
28
+ contractDefinition &&
29
+ !(order.contract && order.contract.student_signed_on)
30
+ );
31
+ }
32
+ }
@@ -1,12 +1,13 @@
1
- $avatar-size: 100px;
1
+ // when parent em-value is 40px, $avatar-size is 100px
2
+ $avatar-size: 2.5em;
2
3
 
3
4
  .dashboard {
4
5
  &__avatar {
5
6
  box-shadow: r-theme-val(dashboard-sidebar, base-shadow);
6
7
  background-color: r-theme-val(dashboard-avatar, background-color);
7
8
  border-radius: 100%;
8
- width: $avatar-size;
9
9
  height: $avatar-size;
10
+ width: $avatar-size;
10
11
  display: flex;
11
12
  justify-content: center;
12
13
  align-items: center;
@@ -14,7 +15,7 @@ $avatar-size: 100px;
14
15
  padding: rem-calc(3px);
15
16
 
16
17
  &__letter {
17
- font-size: 2.5rem;
18
+ font-size: 1em;
18
19
  font-family: $r-font-family-montserrat;
19
20
  font-weight: $font-weight-boldest;
20
21
  line-height: 1;
@@ -8,7 +8,7 @@ export enum DashboardAvatarVariantEnum {
8
8
  SQUARE = 'square',
9
9
  }
10
10
 
11
- interface DashboardAvatarProps {
11
+ export interface DashboardAvatarProps {
12
12
  title: string;
13
13
  image?: Nullable<JoanieFile>;
14
14
  variant?: DashboardAvatarVariantEnum;
@@ -14,10 +14,11 @@ import {
14
14
  import { Spinner } from 'components/Spinner';
15
15
  import Banner, { BannerType } from 'components/Banner';
16
16
  import { Icon, IconTypeEnum } from 'components/Icon';
17
+
17
18
  import useDateFormat from 'hooks/useDateFormat';
18
- import { orderNeedsSignature } from 'widgets/Dashboard/components/DashboardItem/utils/order';
19
19
  import { RouterButton } from 'widgets/Dashboard/components/RouterButton';
20
20
  import { useEnroll } from 'widgets/Dashboard/hooks/useEnroll';
21
+ import { OrderHelper } from 'utils/OrderHelper';
21
22
  import useCourseRunPeriodMessage from './hooks/useCourseRunPeriodMessage';
22
23
 
23
24
  const messages = defineMessages({
@@ -212,7 +213,9 @@ export const DashboardItemCourseEnrollingRun = ({
212
213
  const intl = useIntl();
213
214
  const formatDate = useDateFormat();
214
215
  const courseRunPeriodMessage = useCourseRunPeriodMessage(courseRun, selected);
215
- const haveToSignContract = order ? orderNeedsSignature(order, product) : false;
216
+ const haveToSignContract = order
217
+ ? OrderHelper.orderNeedsSignature(order, product?.contract_definition)
218
+ : false;
216
219
  const isOpenedForEnrollment = useMemo(
217
220
  () => courseRun.state.priority < Priority.FUTURE_NOT_YET_OPEN,
218
221
  [courseRun],
@@ -6,8 +6,8 @@ import { CertificateProduct, Enrollment, ProductType } from 'types/Joanie';
6
6
  import DownloadCertificateButton from 'components/DownloadCertificateButton';
7
7
  import { useCertificate } from 'hooks/useCertificates';
8
8
  import { isOpenedCourseRunCertificate } from 'utils/CourseRuns';
9
+ import { OrderHelper } from 'utils/OrderHelper';
9
10
  import CertificateStatus from '../../CertificateStatus';
10
- import { getActiveEnrollmentOrder } from '../../utils/order';
11
11
 
12
12
  const messages = defineMessages({
13
13
  buyProductCertificateLabel: {
@@ -37,7 +37,7 @@ const ProductCertificateFooter = ({ product, enrollment }: ProductCertificateFoo
37
37
  return null;
38
38
  }
39
39
  const [activeOrder, setActiveOrder] = useState(
40
- getActiveEnrollmentOrder(enrollment.orders || [], product.id),
40
+ OrderHelper.getActiveEnrollmentOrder(enrollment.orders || [], product.id),
41
41
  );
42
42
  const { item: certificate } = useCertificate(activeOrder?.certificate_id);
43
43
 
@@ -10,7 +10,7 @@ import { RouterButton } from 'widgets/Dashboard/components/RouterButton';
10
10
  import { LearnerDashboardPaths } from 'widgets/Dashboard/utils/learnerRouteMessages';
11
11
  import { getDashboardRoutePath } from 'widgets/Dashboard/utils/dashboardRoutes';
12
12
  import { useCourseProduct } from 'hooks/useCourseProducts';
13
- import { orderNeedsSignature } from 'widgets/Dashboard/components/DashboardItem/utils/order';
13
+ import { OrderHelper } from 'utils/OrderHelper';
14
14
 
15
15
  import { DashboardSubItemsList } from '../DashboardSubItemsList';
16
16
  import { DashboardItemCourseEnrolling } from '../CourseEnrolling';
@@ -88,7 +88,7 @@ export const DashboardItemOrder = ({
88
88
  states: { isFetched: isCourseProductRelationFetched },
89
89
  } = useCourseProduct({ product_id: order.product_id, course_id: course.code });
90
90
  const { product } = courseProductRelation || {};
91
- const needsSignature = orderNeedsSignature(order, product);
91
+ const needsSignature = OrderHelper.orderNeedsSignature(order, product?.contract_definition);
92
92
  const getRoutePath = getDashboardRoutePath(useIntl());
93
93
 
94
94
  return (
@@ -98,7 +98,7 @@ export const DashboardItemOrder = ({
98
98
  key={`DashboardItemOrderContract_${order.id}`}
99
99
  title={product.title}
100
100
  order={order}
101
- contract_definition={product.contract_definition!}
101
+ contract_definition={product?.contract_definition!}
102
102
  contract={order.contract}
103
103
  writable={writable}
104
104
  mode="compact"
@@ -114,7 +114,10 @@ export const DashboardItemOrder = ({
114
114
  <div className="dashboard-item-order__footer">
115
115
  <div className="dashboard-item__block__status">
116
116
  <Icon name={IconTypeEnum.SCHOOL} />
117
- <OrderStateMessage order={order} product={product} />
117
+ <OrderStateMessage
118
+ order={order}
119
+ contractDefinition={product?.contract_definition}
120
+ />
118
121
  </div>
119
122
  {showDetailsButton && (
120
123
  <RouterButton
@@ -132,7 +135,7 @@ export const DashboardItemOrder = ({
132
135
  key={`DashboardItemOrderContract_${order.id}`}
133
136
  title={product.title}
134
137
  order={order}
135
- contract_definition={product.contract_definition!}
138
+ contract_definition={product?.contract_definition!}
136
139
  contract={order.contract}
137
140
  writable={writable}
138
141
  mode="compact"
@@ -173,7 +176,7 @@ export const DashboardItemOrder = ({
173
176
  key={`DashboardItemOrderContract_${order.id}`}
174
177
  title={product.title}
175
178
  order={order}
176
- contract_definition={product.contract_definition!}
179
+ contract_definition={product?.contract_definition!}
177
180
  contract={order.contract}
178
181
  writable={writable}
179
182
  mode="compact"
@@ -5,7 +5,6 @@ import {
5
5
  ContractDefinitionFactory,
6
6
  ContractFactory,
7
7
  CredentialOrderFactory,
8
- ProductFactory,
9
8
  } from 'utils/test/factories/joanie';
10
9
  import { OrderState } from 'types/Joanie';
11
10
  import OrderStateMessage, { messages } from '.';
@@ -74,13 +73,11 @@ describe('<DashboardItemOrder/>', () => {
74
73
  contract: null,
75
74
  }).one();
76
75
 
77
- const product = ProductFactory({
78
- contract_definition: ContractDefinitionFactory().one(),
79
- }).one();
76
+ const contractDefinition = ContractDefinitionFactory().one();
80
77
 
81
78
  render(
82
79
  <Wrapper>
83
- <OrderStateMessage order={order} product={product} />
80
+ <OrderStateMessage order={order} contractDefinition={contractDefinition} />
84
81
  </Wrapper>,
85
82
  );
86
83
  expect(screen.getByText('Signature required')).toBeInTheDocument();
@@ -1,9 +1,9 @@
1
1
  import { FormattedMessage, defineMessages } from 'react-intl';
2
2
  import { useEffect } from 'react';
3
- import { CertificateOrder, CredentialOrder, OrderState, Product } from 'types/Joanie';
3
+ import { CertificateOrder, CredentialOrder, OrderState, ContractDefinition } from 'types/Joanie';
4
4
  import { StringHelper } from 'utils/StringHelper';
5
5
  import { handle } from 'utils/errors/handle';
6
- import { orderNeedsSignature } from 'widgets/Dashboard/components/DashboardItem/utils/order';
6
+ import { OrderHelper } from 'utils/OrderHelper';
7
7
 
8
8
  export const messages = defineMessages({
9
9
  statusDraft: {
@@ -53,10 +53,10 @@ export const messages = defineMessages({
53
53
 
54
54
  interface OrderStateMessageProps {
55
55
  order: CredentialOrder | CertificateOrder;
56
- product?: Product;
56
+ contractDefinition?: ContractDefinition;
57
57
  }
58
58
 
59
- const OrderStateMessage = ({ order, product }: OrderStateMessageProps) => {
59
+ const OrderStateMessage = ({ order, contractDefinition }: OrderStateMessageProps) => {
60
60
  const { certificate_id: certificateId } = order;
61
61
  const orderStatusMessages = {
62
62
  [OrderState.DRAFT]: messages.statusDraft,
@@ -72,7 +72,7 @@ const OrderStateMessage = ({ order, product }: OrderStateMessageProps) => {
72
72
  }, [order.state]);
73
73
 
74
74
  if (order.state === OrderState.VALIDATED) {
75
- if (orderNeedsSignature(order, product)) {
75
+ if (OrderHelper.orderNeedsSignature(order, contractDefinition)) {
76
76
  return <FormattedMessage {...messages.statusWaitingSignature} />;
77
77
  }
78
78
 
@@ -0,0 +1,8 @@
1
+ .dashboard-list-avatar {
2
+ &__container {
3
+ position: relative;
4
+ display: flex;
5
+ justify-content: center;
6
+ font-size: rem-calc(12px);
7
+ }
8
+ }
@@ -0,0 +1,11 @@
1
+ import { DashboardAvatar, DashboardAvatarProps } from '../DashboardAvatar';
2
+
3
+ const DashboardListAvatar = (props: DashboardAvatarProps) => {
4
+ return (
5
+ <div className="dashboard-list-avatar__container">
6
+ <DashboardAvatar {...props} />
7
+ </div>
8
+ );
9
+ };
10
+
11
+ export default DashboardListAvatar;
@@ -7,7 +7,7 @@ import Banner, { BannerType } from 'components/Banner';
7
7
  import { useCourseProduct } from 'hooks/useCourseProducts';
8
8
  import { isCredentialOrder } from 'pages/DashboardCourses/useOrdersEnrollments';
9
9
  import { handle } from 'utils/errors/handle';
10
- import { orderNeedsSignature } from 'widgets/Dashboard/components/DashboardItem/utils/order';
10
+ import { OrderHelper } from 'utils/OrderHelper';
11
11
  import { DashboardItemOrder } from '../DashboardItem/Order/DashboardItemOrder';
12
12
 
13
13
  const messages = defineMessages({
@@ -50,7 +50,10 @@ export const DashboardOrderLoader = () => {
50
50
  const error = errorOrder || errorCourseProduct || wrongLinkedProductError;
51
51
 
52
52
  const fetching = fetchingOrder || fetchingCourseProduct;
53
- const needsSignature = orderNeedsSignature(order, courseProduct?.product);
53
+ const needsSignature = OrderHelper.orderNeedsSignature(
54
+ order,
55
+ courseProduct?.product.contract_definition,
56
+ );
54
57
 
55
58
  return (
56
59
  <>
@@ -30,6 +30,8 @@
30
30
  &__avatar {
31
31
  position: absolute;
32
32
  top: calc($avatar-size / -2);
33
+ // avatar's parent font-size define the avatar size.
34
+ font-size: rem-calc(40px);
33
35
  }
34
36
 
35
37
  h3 {
@@ -0,0 +1,58 @@
1
+ import { defineMessages, useIntl } from 'react-intl';
2
+ import { Select, SelectProps } from '@openfun/cunningham-react';
3
+ import { useEffect } from 'react';
4
+ import { useOrganizations } from 'hooks/useOrganizations';
5
+ import { Spinner } from 'components/Spinner';
6
+
7
+ export const messages = defineMessages({
8
+ organizationFilterLabel: {
9
+ defaultMessage: 'Organization',
10
+ description: 'Use as organization filter label',
11
+ id: 'components.ListFilterOrganization.organizationFilterLabel',
12
+ },
13
+ });
14
+
15
+ interface FilterOrganizationProps {
16
+ defaultValue?: string;
17
+ onChange: ({ organization_id }: { organization_id?: string }) => void;
18
+ }
19
+
20
+ const FilterOrganization = ({ defaultValue, onChange }: FilterOrganizationProps) => {
21
+ const intl = useIntl();
22
+ const {
23
+ items: organizations,
24
+ states: { isFetched },
25
+ } = useOrganizations();
26
+
27
+ const organizationOptions = organizations.map((organization) => ({
28
+ label: organization.title,
29
+ value: organization.id,
30
+ }));
31
+
32
+ const handleChange: SelectProps['onChange'] = (e) => {
33
+ const value = e.target.value as string;
34
+ onChange({ organization_id: value });
35
+ };
36
+
37
+ useEffect(() => {
38
+ if (isFetched && defaultValue === undefined) {
39
+ onChange({ organization_id: organizationOptions[0]?.value });
40
+ }
41
+ }, [defaultValue, isFetched]);
42
+
43
+ if (!isFetched) return <Spinner />;
44
+
45
+ return (
46
+ <Select
47
+ label={intl.formatMessage(messages.organizationFilterLabel)}
48
+ options={organizationOptions}
49
+ defaultValue={defaultValue || organizationOptions[0].value}
50
+ onChange={handleChange}
51
+ disabled={!isFetched}
52
+ clearable={false}
53
+ searchable={true}
54
+ />
55
+ );
56
+ };
57
+
58
+ export default FilterOrganization;
@@ -0,0 +1,9 @@
1
+ import { PropsWithChildren } from 'react';
2
+
3
+ const FiltersBar = ({ children }: PropsWithChildren) => {
4
+ return (
5
+ <div className="dashboard__page__actions-row dashboard__page__actions-row--end">{children}</div>
6
+ );
7
+ };
8
+
9
+ export default FiltersBar;
@@ -12,7 +12,7 @@ import { IntlHelper } from 'utils/IntlHelper';
12
12
  import WebAnalyticsAPIHandler from 'api/web-analytics';
13
13
  import EnrollmentDate from 'components/EnrollmentDate';
14
14
  import { Product } from 'types/Joanie';
15
- import { orderNeedsSignature } from 'widgets/Dashboard/components/DashboardItem/utils/order';
15
+ import { OrderHelper } from 'utils/OrderHelper';
16
16
  import { messages as sharedMessages } from '../CourseRunItem';
17
17
  import CourseRunSection, { messages as sectionMessages } from './CourseRunSection';
18
18
 
@@ -60,7 +60,9 @@ const EnrollableCourseRunList = ({ courseRuns, order, product }: Props) => {
60
60
  const intl = useIntl();
61
61
  const formatDate = useDateFormat();
62
62
  const formRef = useRef<HTMLFormElement>(null);
63
- const needsSignature = order ? orderNeedsSignature(order, product) : false;
63
+ const needsSignature = order
64
+ ? OrderHelper.orderNeedsSignature(order, product.contract_definition)
65
+ : false;
64
66
 
65
67
  const [selectedCourseRun, setSelectedCourseRun] = useState<Maybe<Joanie.CourseRun>>();
66
68
  const [submitted, setSubmitted] = useState(false);
@@ -16,7 +16,7 @@ import { Maybe } from 'types/utils';
16
16
  import useDateFormat from 'hooks/useDateFormat';
17
17
  import { ProductHelper } from 'utils/ProductHelper';
18
18
  import useProductOrder from 'hooks/useProductOrder';
19
- import { orderNeedsSignature } from 'widgets/Dashboard/components/DashboardItem/utils/order';
19
+ import { OrderHelper } from 'utils/OrderHelper';
20
20
  import { handle } from 'utils/errors/handle';
21
21
  import { ProductSignatureHeader } from 'widgets/SyllabusCourseRunsList/components/CourseProductItem/components/ProductSignatureHeader';
22
22
  import CertificateItem from './components/CourseProductCertificateItem';
@@ -129,7 +129,9 @@ const Header = ({ product, order, hasPurchased, canPurchase, compact }: HeaderPr
129
129
  );
130
130
  };
131
131
  const Content = ({ product, order }: { product: Product; order?: CredentialOrder }) => {
132
- const needsSignature = order ? orderNeedsSignature(order, product) : false;
132
+ const needsSignature = order
133
+ ? OrderHelper.orderNeedsSignature(order, product.contract_definition)
134
+ : false;
133
135
  const targetCourses = useMemo(() => {
134
136
  if (order) {
135
137
  return order.target_courses;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "richie-education",
3
- "version": "2.25.0-b2.dev83",
3
+ "version": "2.25.0-b2.dev91",
4
4
  "description": "A CMS to build learning portals for Open Education",
5
5
  "main": "sandbox/manage.py",
6
6
  "scripts": {
@@ -33,6 +33,7 @@
33
33
  @import '../../js/widgets/Dashboard/components/DashboardItem/Contract/_styles';
34
34
  @import '../../js/widgets/Dashboard/components/DashboardItem/styles';
35
35
  @import '../../js/widgets/Dashboard/components/DashboardLayout/styles';
36
+ @import '../../js/widgets/Dashboard/components/DashboardListAvatar/styles';
36
37
  @import '../../js/widgets/Dashboard/components/DashboardOrderLoader/styles';
37
38
  @import '../../js/widgets/Dashboard/components/DashboardBreadcrumbs/styles';
38
39
  @import '../../js/widgets/Dashboard/components/DashboardSidebar/styles';
@@ -28,14 +28,7 @@ $r-course-glimpse-gutter: 0.8rem !default;
28
28
 
29
29
  &__count {
30
30
  margin-right: $r-course-glimpse-gutter;
31
- padding: 0;
32
- flex-basis: 100%; // Should not wrap with actual course glimpses
33
- color: r-theme-val(course-glimpse-list, count-color);
34
- text-align: right;
35
-
36
31
  @include media-breakpoint-up(lg) {
37
- padding: 0;
38
-
39
32
  &:first-child {
40
33
  margin-top: -1rem; // Cancel out top padding
41
34
  }
@@ -2,6 +2,7 @@
2
2
  @import './banner';
3
3
  @import './breadcrumbs';
4
4
  @import './dashboard';
5
+ @import './list';
5
6
  @import './course_glimpses';
6
7
  @import './blogpost_glimpses';
7
8
  @import './organization_glimpses';
@@ -0,0 +1,8 @@
1
+ .list {
2
+ &__count-description {
3
+ flex-basis: 100%; // Should not wrap as mozaic list elements
4
+ color: r-theme-val(course-glimpse-list, count-color);
5
+ text-align: right;
6
+ align-self: self-end;
7
+ }
8
+ }
@@ -1,15 +0,0 @@
1
- import { OrderEnrollment, ACTIVE_ORDER_STATES, Order, Product, OrderState } from 'types/Joanie';
2
-
3
- export const getActiveEnrollmentOrder = (orders: OrderEnrollment[], productId: string) => {
4
- const filter = (order: OrderEnrollment) =>
5
- ACTIVE_ORDER_STATES.includes(order.state) && order.product_id === productId;
6
- return orders.find(filter);
7
- };
8
-
9
- export const orderNeedsSignature = (order: Order, product?: Product) => {
10
- return (
11
- order?.state === OrderState.VALIDATED &&
12
- product?.contract_definition &&
13
- !(order.contract && order.contract.student_signed_on)
14
- );
15
- };