@canva/cli 1.11.0 → 1.13.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.
Files changed (78) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +13 -0
  3. package/cli.js +593 -580
  4. package/lib/cjs/index.cjs +3 -3
  5. package/lib/esm/index.mjs +4 -4
  6. package/package.json +7 -2
  7. package/templates/base/package.json +7 -7
  8. package/templates/base/webpack.config.ts +0 -1
  9. package/templates/common/jest.config.mjs +1 -1
  10. package/templates/dam/backend/server.ts +8 -0
  11. package/templates/dam/canva-app.json +4 -0
  12. package/templates/dam/package.json +7 -7
  13. package/templates/dam/webpack.config.ts +0 -1
  14. package/templates/data_connector/README.md +1 -1
  15. package/templates/data_connector/package.json +7 -7
  16. package/templates/data_connector/webpack.config.ts +0 -1
  17. package/templates/gen_ai/backend/server.ts +17 -0
  18. package/templates/gen_ai/package.json +7 -7
  19. package/templates/gen_ai/src/api/api.ts +4 -0
  20. package/templates/gen_ai/webpack.config.ts +0 -1
  21. package/templates/hello_world/package.json +7 -7
  22. package/templates/hello_world/webpack.config.ts +0 -1
  23. package/templates/mls/README.md +81 -0
  24. package/templates/mls/canva-app.json +25 -0
  25. package/templates/mls/declarations/declarations.d.ts +29 -0
  26. package/templates/mls/eslint.config.mjs +14 -0
  27. package/templates/mls/jest.config.mjs +36 -0
  28. package/templates/mls/jest.setup.ts +37 -0
  29. package/templates/mls/package.json +117 -0
  30. package/templates/mls/scripts/copy_env.ts +13 -0
  31. package/templates/mls/scripts/ssl/ssl.ts +131 -0
  32. package/templates/mls/scripts/start/app_runner.ts +223 -0
  33. package/templates/mls/scripts/start/context.ts +171 -0
  34. package/templates/mls/scripts/start/start.ts +46 -0
  35. package/templates/mls/src/__tests__/app.tests.tsx +11 -0
  36. package/templates/mls/src/__tests__/office_selection_page.tests.tsx +72 -0
  37. package/templates/mls/src/__tests__/utils.tsx +19 -0
  38. package/templates/mls/src/adapter.ts +126 -0
  39. package/templates/mls/src/components/agent/agent_card.tsx +57 -0
  40. package/templates/mls/src/components/agent/agent_grid.tsx +37 -0
  41. package/templates/mls/src/components/agent/agent_list.tsx +17 -0
  42. package/templates/mls/src/components/agent/agent_search_filters.tsx +88 -0
  43. package/templates/mls/src/components/breadcrumb/breadcrumb.tsx +40 -0
  44. package/templates/mls/src/components/listing/listing_card.tsx +64 -0
  45. package/templates/mls/src/components/listing/listing_grid.tsx +37 -0
  46. package/templates/mls/src/components/listing/listing_list.tsx +21 -0
  47. package/templates/mls/src/components/listing/listing_search_filters.tsx +145 -0
  48. package/templates/mls/src/components/placeholders/placeholders.tsx +65 -0
  49. package/templates/mls/src/data.ts +359 -0
  50. package/templates/mls/src/index.tsx +4 -0
  51. package/templates/mls/src/intents/design_editor/app.tsx +44 -0
  52. package/templates/mls/src/intents/design_editor/index.tsx +25 -0
  53. package/templates/mls/src/pages/agent_details_page/agent_details_page.tsx +175 -0
  54. package/templates/mls/src/pages/list_page/agent_tab_panel.tsx +126 -0
  55. package/templates/mls/src/pages/list_page/list_page.tsx +67 -0
  56. package/templates/mls/src/pages/list_page/listing_tab_panel.tsx +135 -0
  57. package/templates/mls/src/pages/listing_details_page/listing_details_page.tsx +418 -0
  58. package/templates/mls/src/pages/loading_page/loading_page.tsx +152 -0
  59. package/templates/mls/src/pages/office_selection_page/office_selection_page.tsx +144 -0
  60. package/templates/mls/src/real_estate.type.ts +44 -0
  61. package/templates/mls/src/util/use_add_element.tsx +62 -0
  62. package/templates/mls/src/util/use_drag_element.tsx +68 -0
  63. package/templates/mls/styles/components.css +56 -0
  64. package/templates/mls/tsconfig.json +55 -0
  65. package/templates/mls/webpack.config.ts +253 -0
  66. package/templates/base/backend/routers/oauth.ts +0 -393
  67. package/templates/base/utils/backend/bearer_middleware/bearer_middleware.ts +0 -99
  68. package/templates/base/utils/backend/bearer_middleware/index.ts +0 -1
  69. package/templates/base/utils/backend/bearer_middleware/tests/bearer_middleware.tests.ts +0 -192
  70. package/templates/common/utils/backend/base_backend/create.ts +0 -104
  71. package/templates/gen_ai/backend/database/database.ts +0 -42
  72. package/templates/gen_ai/utils/backend/bearer_middleware/bearer_middleware.ts +0 -99
  73. package/templates/gen_ai/utils/backend/bearer_middleware/index.ts +0 -1
  74. /package/templates/base/{utils/backend → backend}/base_backend/create.ts +0 -0
  75. /package/templates/base/{utils/backend → backend}/jwt_middleware/index.ts +0 -0
  76. /package/templates/base/{utils/backend → backend}/jwt_middleware/jwt_middleware.ts +0 -0
  77. /package/templates/{common → gen_ai}/utils/backend/jwt_middleware/index.ts +0 -0
  78. /package/templates/{common → gen_ai}/utils/backend/jwt_middleware/jwt_middleware.ts +0 -0
@@ -0,0 +1,57 @@
1
+ import { HorizontalCard, ImageCard } from "@canva/app-ui-kit";
2
+ import React from "react";
3
+ import { useIntl } from "react-intl";
4
+ import type { Agent } from "../../real_estate.type";
5
+
6
+ interface AgentCardProps {
7
+ item: Agent;
8
+ onClick: (item: Agent) => void;
9
+ }
10
+
11
+ export const ListAgentCard: React.FC<AgentCardProps> = ({ item, onClick }) => {
12
+ const intl = useIntl();
13
+ const headshot = item.headshots?.[0];
14
+
15
+ return (
16
+ <HorizontalCard
17
+ ariaLabel={item.name}
18
+ title={item.name}
19
+ description={item.officeId}
20
+ onClick={() => onClick(item)}
21
+ thumbnail={
22
+ headshot
23
+ ? {
24
+ url: headshot.url,
25
+ alt: intl.formatMessage(
26
+ {
27
+ defaultMessage: "Profile photo of {name}",
28
+ description: "Alt text for agent profile photo",
29
+ },
30
+ { name: item.name },
31
+ ),
32
+ }
33
+ : undefined
34
+ }
35
+ />
36
+ );
37
+ };
38
+
39
+ export const GridAgentCard: React.FC<AgentCardProps> = ({ item, onClick }) => {
40
+ const intl = useIntl();
41
+
42
+ return (
43
+ <ImageCard
44
+ selectable
45
+ thumbnailUrl={item.headshots?.[0]?.url || ""}
46
+ alt={intl.formatMessage(
47
+ {
48
+ defaultMessage: "Profile photo of {name}",
49
+ description: "Alt text for agent profile photo",
50
+ },
51
+ { name: item.name },
52
+ )}
53
+ thumbnailHeight={150}
54
+ onClick={() => onClick(item)}
55
+ />
56
+ );
57
+ };
@@ -0,0 +1,37 @@
1
+ import { Column, Columns, Rows } from "@canva/app-ui-kit";
2
+ import type { Agent } from "../../real_estate.type";
3
+ import { GridAgentCard } from "./agent_card";
4
+
5
+ interface AgentGridProps {
6
+ agents: Agent[];
7
+ onAgentClick: (item: Agent) => void;
8
+ }
9
+
10
+ export const AgentGrid = ({ agents, onAgentClick }: AgentGridProps) => {
11
+ return (
12
+ <Rows spacing="2u">
13
+ {agents.map((item, index) => {
14
+ if (index % 2 === 0) {
15
+ const nextItem = agents[index + 1];
16
+ return (
17
+ <Columns
18
+ key={`row-${Math.floor(index / 2)}`}
19
+ spacing="2u"
20
+ alignY="stretch"
21
+ >
22
+ <Column key={item.id} width="1/2">
23
+ <GridAgentCard item={item} onClick={onAgentClick} />
24
+ </Column>
25
+ {nextItem && (
26
+ <Column key={nextItem.id} width="1/2">
27
+ <GridAgentCard item={nextItem} onClick={onAgentClick} />
28
+ </Column>
29
+ )}
30
+ </Columns>
31
+ );
32
+ }
33
+ return null;
34
+ })}
35
+ </Rows>
36
+ );
37
+ };
@@ -0,0 +1,17 @@
1
+ import type { Agent } from "../../real_estate.type";
2
+ import { ListAgentCard } from "./agent_card";
3
+
4
+ interface AgentListProps {
5
+ agents: Agent[];
6
+ onAgentClick: (item: Agent) => void;
7
+ }
8
+
9
+ export const AgentList = ({ agents, onAgentClick }: AgentListProps) => {
10
+ return (
11
+ <>
12
+ {agents.map((item: Agent) => (
13
+ <ListAgentCard key={item.id} item={item} onClick={onAgentClick} />
14
+ ))}
15
+ </>
16
+ );
17
+ };
@@ -0,0 +1,88 @@
1
+ import {
2
+ Button,
3
+ Column,
4
+ Columns,
5
+ GridViewIcon,
6
+ ListBulletLtrIcon,
7
+ Rows,
8
+ SearchInputMenu,
9
+ Select,
10
+ } from "@canva/app-ui-kit";
11
+ import { useState } from "react";
12
+ import { useIntl } from "react-intl";
13
+
14
+ type Layout = "grid" | "list";
15
+
16
+ interface AgentSearchFiltersProps {
17
+ query: string;
18
+ onQueryChange: (query: string) => void;
19
+ layout: Layout;
20
+ onLayoutToggle: () => void;
21
+ sort: string;
22
+ onSortChange: (sort: string) => void;
23
+ }
24
+
25
+ export const AgentSearchFilters = ({
26
+ query,
27
+ onQueryChange,
28
+ layout,
29
+ onLayoutToggle,
30
+ sort,
31
+ onSortChange,
32
+ }: AgentSearchFiltersProps) => {
33
+ const intl = useIntl();
34
+ const [queryValue, setQueryValue] = useState(query);
35
+ return (
36
+ <Rows spacing="2u">
37
+ <SearchInputMenu
38
+ value={queryValue}
39
+ onChange={setQueryValue}
40
+ onChangeComplete={() => onQueryChange(queryValue)}
41
+ onClear={() => {
42
+ setQueryValue("");
43
+ onQueryChange("");
44
+ }}
45
+ placeholder={intl.formatMessage({
46
+ defaultMessage: "Search agents...",
47
+ description: "Placeholder text for agents search input",
48
+ })}
49
+ />
50
+ <Columns spacing="1u" alignY="center">
51
+ <Column width="content">
52
+ <Button
53
+ icon={layout === "grid" ? GridViewIcon : ListBulletLtrIcon}
54
+ type="button"
55
+ onClick={onLayoutToggle}
56
+ variant="secondary"
57
+ />
58
+ </Column>
59
+ <Column width="1/3">
60
+ <Select
61
+ value={sort}
62
+ onChange={onSortChange}
63
+ options={[
64
+ {
65
+ label: intl.formatMessage({
66
+ defaultMessage: "Name: A to Z",
67
+ description: "Sort option - name ascending",
68
+ }),
69
+ value: "name-asc",
70
+ },
71
+ {
72
+ label: intl.formatMessage({
73
+ defaultMessage: "Name: Z to A",
74
+ description: "Sort option - name descending",
75
+ }),
76
+ value: "name-desc",
77
+ },
78
+ ]}
79
+ placeholder={intl.formatMessage({
80
+ defaultMessage: "Sort by",
81
+ description: "Placeholder text for agents sort dropdown",
82
+ })}
83
+ />
84
+ </Column>
85
+ </Columns>
86
+ </Rows>
87
+ );
88
+ };
@@ -0,0 +1,40 @@
1
+ import {
2
+ ArrowLeftIcon,
3
+ Button,
4
+ Column,
5
+ Columns,
6
+ Text,
7
+ } from "@canva/app-ui-kit";
8
+ import { useIntl } from "react-intl";
9
+ import { useLocation, useNavigate } from "react-router-dom";
10
+ import type { Office } from "../../real_estate.type";
11
+
12
+ export const Breadcrumb = () => {
13
+ const office = (useLocation().state as { office: Office })?.office;
14
+ const navigate = useNavigate();
15
+ const intl = useIntl();
16
+
17
+ return (
18
+ <Columns spacing="1u" alignY="center">
19
+ <Column width="content">
20
+ <Button
21
+ icon={ArrowLeftIcon}
22
+ size="small"
23
+ type="button"
24
+ variant="tertiary"
25
+ onClick={() => navigate("/entry")}
26
+ />
27
+ </Column>
28
+ <Column>
29
+ <Text variant="bold">
30
+ {office
31
+ ? office.name
32
+ : intl.formatMessage({
33
+ defaultMessage: "Back",
34
+ description: "Back button",
35
+ })}
36
+ </Text>
37
+ </Column>
38
+ </Columns>
39
+ );
40
+ };
@@ -0,0 +1,64 @@
1
+ import { HorizontalCard, ImageCard, Rows, Text } from "@canva/app-ui-kit";
2
+ import type { Property } from "../../real_estate.type";
3
+
4
+ export interface ListingCardProps<T extends Property> {
5
+ item: T;
6
+ onClick?: (item: T) => void;
7
+ }
8
+
9
+ interface BaseListingCardProps<T extends Property> {
10
+ item: T;
11
+ onClick?: (item: T) => void;
12
+ }
13
+
14
+ export const GridListingCard = <T extends Property>({
15
+ item,
16
+ onClick,
17
+ }: BaseListingCardProps<T>) => {
18
+ const handleClick = () => {
19
+ onClick?.(item);
20
+ };
21
+
22
+ return (
23
+ <div>
24
+ <Rows spacing="1u">
25
+ <ImageCard
26
+ selectable
27
+ thumbnailUrl={item.thumbnail.url}
28
+ alt={item.description}
29
+ borderRadius="standard"
30
+ onClick={handleClick}
31
+ thumbnailHeight={110}
32
+ />
33
+ <Rows spacing="0">
34
+ <Text variant="bold" lineClamp={1}>
35
+ {item.title}
36
+ </Text>
37
+ {item.suburb && <Text size="small">{item.suburb}</Text>}
38
+ </Rows>
39
+ </Rows>
40
+ </div>
41
+ );
42
+ };
43
+
44
+ export const ListListingCard = <T extends Property>({
45
+ item,
46
+ onClick,
47
+ }: BaseListingCardProps<T>) => {
48
+ const handleClick = () => {
49
+ onClick?.(item);
50
+ };
51
+
52
+ return (
53
+ <HorizontalCard
54
+ ariaLabel={item.title}
55
+ title={item.title}
56
+ description={item.suburb}
57
+ onClick={handleClick}
58
+ thumbnail={{
59
+ url: item.thumbnail.url,
60
+ alt: item.description,
61
+ }}
62
+ />
63
+ );
64
+ };
@@ -0,0 +1,37 @@
1
+ import { Column, Columns, Rows } from "@canva/app-ui-kit";
2
+ import type { Property } from "../../real_estate.type";
3
+ import { GridListingCard } from "./listing_card";
4
+
5
+ interface ListingGridProps {
6
+ listings: Property[];
7
+ onListingClick: (item: Property) => void;
8
+ }
9
+
10
+ export const ListingGrid = ({ listings, onListingClick }: ListingGridProps) => {
11
+ return (
12
+ <Rows spacing="2u">
13
+ {listings.map((item, index) => {
14
+ if (index % 2 === 0) {
15
+ const nextItem = listings[index + 1];
16
+ return (
17
+ <Columns
18
+ key={`row-${Math.floor(index / 2)}`}
19
+ spacing="2u"
20
+ alignY="stretch"
21
+ >
22
+ <Column key={item.id} width="1/2">
23
+ <GridListingCard item={item} onClick={onListingClick} />
24
+ </Column>
25
+ {nextItem && (
26
+ <Column key={nextItem.id} width="1/2">
27
+ <GridListingCard item={nextItem} onClick={onListingClick} />
28
+ </Column>
29
+ )}
30
+ </Columns>
31
+ );
32
+ }
33
+ return null;
34
+ })}
35
+ </Rows>
36
+ );
37
+ };
@@ -0,0 +1,21 @@
1
+ import type { Property } from "../../real_estate.type";
2
+ import { ListListingCard } from "./listing_card";
3
+
4
+ interface ListingListProps {
5
+ listings: Property[];
6
+ onListingClick: (item: Property) => void;
7
+ }
8
+
9
+ export const ListingList = ({ listings, onListingClick }: ListingListProps) => {
10
+ return (
11
+ <>
12
+ {listings.map((item: Property, index: number) => (
13
+ <ListListingCard
14
+ key={`${item.id}-${index}`}
15
+ item={item}
16
+ onClick={onListingClick}
17
+ />
18
+ ))}
19
+ </>
20
+ );
21
+ };
@@ -0,0 +1,145 @@
1
+ import {
2
+ Button,
3
+ Column,
4
+ Columns,
5
+ GridViewIcon,
6
+ ListBulletLtrIcon,
7
+ Rows,
8
+ SearchInputMenu,
9
+ Select,
10
+ } from "@canva/app-ui-kit";
11
+ import { useState } from "react";
12
+ import { useIntl } from "react-intl";
13
+
14
+ type Layout = "grid" | "list";
15
+
16
+ interface ListingSearchFiltersProps {
17
+ query: string;
18
+ onQueryChange: (query: string) => void;
19
+ layout: Layout;
20
+ onLayoutToggle: () => void;
21
+ propertyType: string;
22
+ onPropertyTypeChange: (type: string) => void;
23
+ sort: string;
24
+ onSortChange: (sort: string) => void;
25
+ }
26
+
27
+ export const ListingSearchFilters = ({
28
+ query,
29
+ onQueryChange,
30
+ layout,
31
+ onLayoutToggle,
32
+ propertyType,
33
+ onPropertyTypeChange,
34
+ sort,
35
+ onSortChange,
36
+ }: ListingSearchFiltersProps) => {
37
+ const intl = useIntl();
38
+ const [queryValue, setQueryValue] = useState(query);
39
+ return (
40
+ <Rows spacing="2u">
41
+ <SearchInputMenu
42
+ value={queryValue}
43
+ onChange={setQueryValue}
44
+ onChangeComplete={() => onQueryChange(queryValue)}
45
+ onClear={() => {
46
+ setQueryValue("");
47
+ onQueryChange("");
48
+ }}
49
+ placeholder={intl.formatMessage({
50
+ defaultMessage: "Search listings...",
51
+ description: "Placeholder text for listings search input",
52
+ })}
53
+ />
54
+ <Columns spacing="1u" alignY="center">
55
+ <Column width="content">
56
+ <Button
57
+ icon={layout === "grid" ? GridViewIcon : ListBulletLtrIcon}
58
+ type="button"
59
+ onClick={onLayoutToggle}
60
+ variant="secondary"
61
+ ariaLabel={intl.formatMessage(
62
+ {
63
+ defaultMessage:
64
+ "Toggle layout between grid and list. Current layout is {currentLayout}",
65
+ description: "Aria label for layout toggle button",
66
+ },
67
+ {
68
+ currentLayout:
69
+ layout === "grid"
70
+ ? intl.formatMessage({
71
+ defaultMessage: "grid",
72
+ description: "Layout option",
73
+ })
74
+ : intl.formatMessage({
75
+ defaultMessage: "list",
76
+ description: "Layout option",
77
+ }),
78
+ },
79
+ )}
80
+ />
81
+ </Column>
82
+ <Column width="1/3">
83
+ {/* TODO (App Developer): Review filter options and update to match your specific property data shape */}
84
+ <Select
85
+ value={propertyType}
86
+ onChange={onPropertyTypeChange}
87
+ options={[
88
+ {
89
+ label: intl.formatMessage({
90
+ defaultMessage: "House",
91
+ description: "Property type option - house",
92
+ }),
93
+ value: "house",
94
+ },
95
+ {
96
+ label: intl.formatMessage({
97
+ defaultMessage: "Apartment",
98
+ description: "Property type option - apartment",
99
+ }),
100
+ value: "apartment",
101
+ },
102
+ {
103
+ label: intl.formatMessage({
104
+ defaultMessage: "Townhouse",
105
+ description: "Property type option - townhouse",
106
+ }),
107
+ value: "townhouse",
108
+ },
109
+ ]}
110
+ placeholder={intl.formatMessage({
111
+ defaultMessage: "Property type",
112
+ description: "Placeholder text for property type dropdown",
113
+ })}
114
+ />
115
+ </Column>
116
+ <Column width="1/3">
117
+ <Select
118
+ value={sort}
119
+ onChange={onSortChange}
120
+ options={[
121
+ {
122
+ label: intl.formatMessage({
123
+ defaultMessage: "Price: Low to High",
124
+ description: "Sort option - price ascending",
125
+ }),
126
+ value: "price-asc",
127
+ },
128
+ {
129
+ label: intl.formatMessage({
130
+ defaultMessage: "Price: High to Low",
131
+ description: "Sort option - price descending",
132
+ }),
133
+ value: "price-desc",
134
+ },
135
+ ]}
136
+ placeholder={intl.formatMessage({
137
+ defaultMessage: "Sort by",
138
+ description: "Placeholder text for sort dropdown",
139
+ })}
140
+ />
141
+ </Column>
142
+ </Columns>
143
+ </Rows>
144
+ );
145
+ };
@@ -0,0 +1,65 @@
1
+ import {
2
+ Box,
3
+ Column,
4
+ Columns,
5
+ Placeholder,
6
+ Rows,
7
+ TextPlaceholder,
8
+ TitlePlaceholder,
9
+ } from "@canva/app-ui-kit";
10
+ import React from "react";
11
+
12
+ export const ListPlaceholder = () => (
13
+ <Columns spacing="2u" align="start" alignY="stretch">
14
+ <Column width="content">
15
+ <Box width="full" height="full">
16
+ <div
17
+ style={{
18
+ height: "60px",
19
+ width: "60px",
20
+ paddingLeft: "var(--ui-kit-space-050)",
21
+ }}
22
+ >
23
+ <Placeholder shape="rectangle" />
24
+ </div>
25
+ </Box>
26
+ </Column>
27
+ <Column width="fluid">
28
+ <Rows spacing="0">
29
+ <TitlePlaceholder />
30
+ <TextPlaceholder />
31
+ </Rows>
32
+ </Column>
33
+ </Columns>
34
+ );
35
+
36
+ export const GridPlaceholder = () => (
37
+ <Columns spacing="2u" alignY="stretch">
38
+ <Column width="1/2">
39
+ <Rows spacing="1u">
40
+ <Box width="full" height="full">
41
+ <div style={{ height: "120px" }}>
42
+ <Placeholder shape="rectangle" />
43
+ </div>
44
+ </Box>
45
+ <Rows spacing="0">
46
+ <TitlePlaceholder />
47
+ <TitlePlaceholder size="small" />
48
+ </Rows>
49
+ </Rows>
50
+ </Column>
51
+ <Column width="1/2">
52
+ <Rows spacing="1u">
53
+ <Box width="full" height="full">
54
+ <div style={{ height: "120px" }}>
55
+ <Placeholder shape="rectangle" />
56
+ </div>
57
+ </Box>
58
+ <Rows spacing="0">
59
+ <TitlePlaceholder />
60
+ <TitlePlaceholder size="small" />
61
+ </Rows>
62
+ </Rows>
63
+ </Column>
64
+ </Columns>
65
+ );