@canva/cli 1.10.0 → 1.12.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 (126) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +2 -0
  3. package/cli.js +596 -574
  4. package/lib/cjs/index.cjs +2 -2
  5. package/lib/esm/index.mjs +2 -2
  6. package/lib/index.d.ts +2 -0
  7. package/package.json +7 -2
  8. package/templates/base/package.json +9 -8
  9. package/templates/base/styles/components.css +18 -0
  10. package/templates/common/.env.template +1 -1
  11. package/templates/common/jest.config.mjs +1 -1
  12. package/templates/dam/backend/server.ts +8 -0
  13. package/templates/dam/canva-app.json +9 -0
  14. package/templates/dam/package.json +10 -8
  15. package/templates/dam/src/index.tsx +3 -21
  16. package/templates/dam/src/intents/design_editor/index.tsx +25 -0
  17. package/templates/data_connector/README.md +1 -1
  18. package/templates/data_connector/package.json +9 -8
  19. package/templates/data_connector/src/api/data_sources/designs.tsx +1 -1
  20. package/templates/data_connector/src/api/data_sources/templates.tsx +1 -1
  21. package/templates/data_connector/src/components/header.tsx +1 -1
  22. package/templates/data_connector/src/index.tsx +2 -66
  23. package/templates/data_connector/src/{app.tsx → intents/data_connector/app.tsx} +3 -3
  24. package/templates/data_connector/src/{entrypoint.tsx → intents/data_connector/entrypoint.tsx} +5 -5
  25. package/templates/data_connector/src/{home.tsx → intents/data_connector/home.tsx} +1 -1
  26. package/templates/data_connector/src/intents/data_connector/index.tsx +56 -0
  27. package/templates/data_connector/src/pages/error.tsx +1 -1
  28. package/templates/data_connector/src/pages/login.tsx +1 -1
  29. package/templates/data_connector/src/routes/protected_route.tsx +1 -1
  30. package/templates/data_connector/src/routes/routes.tsx +3 -3
  31. package/templates/data_connector/styles/components.css +18 -0
  32. package/templates/gen_ai/backend/server.ts +17 -0
  33. package/templates/gen_ai/canva-app.json +5 -0
  34. package/templates/gen_ai/package.json +10 -8
  35. package/templates/gen_ai/src/api/api.ts +4 -0
  36. package/templates/gen_ai/src/components/footer.tsx +1 -1
  37. package/templates/gen_ai/src/components/loading_results.tsx +1 -1
  38. package/templates/gen_ai/src/components/prompt_input.tsx +1 -1
  39. package/templates/gen_ai/src/index.tsx +3 -14
  40. package/templates/gen_ai/src/{app.tsx → intents/design_editor/app.tsx} +3 -3
  41. package/templates/gen_ai/src/{home.tsx → intents/design_editor/home.tsx} +1 -1
  42. package/templates/gen_ai/src/intents/design_editor/index.tsx +17 -0
  43. package/templates/gen_ai/src/pages/error.tsx +1 -1
  44. package/templates/gen_ai/src/routes/routes.tsx +2 -2
  45. package/templates/gen_ai/styles/components.css +18 -0
  46. package/templates/hello_world/canva-app.json +5 -0
  47. package/templates/hello_world/package.json +10 -8
  48. package/templates/hello_world/src/index.tsx +3 -21
  49. package/templates/hello_world/src/{app.tsx → intents/design_editor/app.tsx} +26 -3
  50. package/templates/hello_world/src/intents/design_editor/index.tsx +25 -0
  51. package/templates/hello_world/src/{tests → intents/design_editor/tests}/app.tests.tsx +19 -13
  52. package/templates/hello_world/styles/components.css +18 -0
  53. package/templates/mls/README.md +81 -0
  54. package/templates/mls/canva-app.json +25 -0
  55. package/templates/mls/declarations/declarations.d.ts +29 -0
  56. package/templates/mls/eslint.config.mjs +14 -0
  57. package/templates/mls/jest.config.mjs +36 -0
  58. package/templates/mls/jest.setup.ts +37 -0
  59. package/templates/mls/package.json +117 -0
  60. package/templates/mls/scripts/copy_env.ts +13 -0
  61. package/templates/mls/scripts/ssl/ssl.ts +131 -0
  62. package/templates/mls/scripts/start/app_runner.ts +223 -0
  63. package/templates/mls/scripts/start/context.ts +171 -0
  64. package/templates/mls/scripts/start/start.ts +46 -0
  65. package/templates/mls/src/__tests__/app.tests.tsx +11 -0
  66. package/templates/mls/src/__tests__/office_selection_page.tests.tsx +72 -0
  67. package/templates/mls/src/__tests__/utils.tsx +19 -0
  68. package/templates/mls/src/adapter.ts +126 -0
  69. package/templates/mls/src/components/agent/agent_card.tsx +57 -0
  70. package/templates/mls/src/components/agent/agent_grid.tsx +37 -0
  71. package/templates/mls/src/components/agent/agent_list.tsx +17 -0
  72. package/templates/mls/src/components/agent/agent_search_filters.tsx +88 -0
  73. package/templates/mls/src/components/breadcrumb/breadcrumb.tsx +40 -0
  74. package/templates/mls/src/components/listing/listing_card.tsx +64 -0
  75. package/templates/mls/src/components/listing/listing_grid.tsx +37 -0
  76. package/templates/mls/src/components/listing/listing_list.tsx +21 -0
  77. package/templates/mls/src/components/listing/listing_search_filters.tsx +145 -0
  78. package/templates/mls/src/components/placeholders/placeholders.tsx +65 -0
  79. package/templates/mls/src/data.ts +359 -0
  80. package/templates/mls/src/index.tsx +4 -0
  81. package/templates/mls/src/intents/design_editor/app.tsx +44 -0
  82. package/templates/mls/src/intents/design_editor/index.tsx +25 -0
  83. package/templates/mls/src/pages/agent_details_page/agent_details_page.tsx +175 -0
  84. package/templates/mls/src/pages/list_page/agent_tab_panel.tsx +126 -0
  85. package/templates/mls/src/pages/list_page/list_page.tsx +67 -0
  86. package/templates/mls/src/pages/list_page/listing_tab_panel.tsx +135 -0
  87. package/templates/mls/src/pages/listing_details_page/listing_details_page.tsx +418 -0
  88. package/templates/mls/src/pages/loading_page/loading_page.tsx +152 -0
  89. package/templates/mls/src/pages/office_selection_page/office_selection_page.tsx +144 -0
  90. package/templates/mls/src/real_estate.type.ts +44 -0
  91. package/templates/mls/src/util/use_add_element.tsx +62 -0
  92. package/templates/mls/src/util/use_drag_element.tsx +68 -0
  93. package/templates/mls/styles/components.css +56 -0
  94. package/templates/mls/tsconfig.json +55 -0
  95. package/templates/mls/webpack.config.ts +254 -0
  96. package/templates/optional/AGENTS.md +80 -2
  97. package/templates/optional/CLAUDE.md +80 -2
  98. package/templates/base/backend/routers/oauth.ts +0 -393
  99. package/templates/base/utils/backend/bearer_middleware/bearer_middleware.ts +0 -99
  100. package/templates/base/utils/backend/bearer_middleware/index.ts +0 -1
  101. package/templates/base/utils/backend/bearer_middleware/tests/bearer_middleware.tests.ts +0 -192
  102. package/templates/base/utils/use_add_element.ts +0 -58
  103. package/templates/base/utils/use_feature_support.ts +0 -28
  104. package/templates/common/utils/backend/base_backend/create.ts +0 -104
  105. package/templates/common/utils/table_wrapper.ts +0 -520
  106. package/templates/common/utils/use_add_element.ts +0 -58
  107. package/templates/common/utils/use_feature_support.ts +0 -28
  108. package/templates/common/utils/use_overlay_hook.ts +0 -76
  109. package/templates/common/utils/use_selection_hook.ts +0 -37
  110. package/templates/gen_ai/backend/database/database.ts +0 -42
  111. package/templates/gen_ai/utils/backend/bearer_middleware/bearer_middleware.ts +0 -99
  112. package/templates/gen_ai/utils/backend/bearer_middleware/index.ts +0 -1
  113. package/templates/hello_world/utils/use_add_element.ts +0 -58
  114. package/templates/hello_world/utils/use_feature_support.ts +0 -28
  115. /package/templates/base/{utils/backend → backend}/base_backend/create.ts +0 -0
  116. /package/templates/base/{utils/backend → backend}/jwt_middleware/index.ts +0 -0
  117. /package/templates/base/{utils/backend → backend}/jwt_middleware/jwt_middleware.ts +0 -0
  118. /package/templates/dam/src/{adapter.ts → intents/design_editor/adapter.ts} +0 -0
  119. /package/templates/dam/src/{app.tsx → intents/design_editor/app.tsx} +0 -0
  120. /package/templates/dam/src/{config.ts → intents/design_editor/config.ts} +0 -0
  121. /package/templates/dam/src/{index.css → intents/design_editor/index.css} +0 -0
  122. /package/templates/data_connector/src/{paths.ts → routes/paths.ts} +0 -0
  123. /package/templates/gen_ai/src/{paths.ts → routes/paths.ts} +0 -0
  124. /package/templates/{common → gen_ai}/utils/backend/jwt_middleware/index.ts +0 -0
  125. /package/templates/{common → gen_ai}/utils/backend/jwt_middleware/jwt_middleware.ts +0 -0
  126. /package/templates/hello_world/src/{tests → intents/design_editor/tests}/__snapshots__/app.tests.tsx.snap +0 -0
@@ -0,0 +1,126 @@
1
+ import { Box, Rows, Scrollable, Text } from "@canva/app-ui-kit";
2
+ import { useInfiniteQuery } from "@tanstack/react-query";
3
+ import { useState } from "react";
4
+ import InfiniteScroll from "react-infinite-scroller";
5
+ import { useIntl } from "react-intl";
6
+ import { useNavigate } from "react-router-dom";
7
+ import { fetchAgents } from "../../adapter";
8
+ import { AgentGrid } from "../../components/agent/agent_grid";
9
+ import { AgentList } from "../../components/agent/agent_list";
10
+ import { AgentSearchFilters } from "../../components/agent/agent_search_filters";
11
+ import {
12
+ GridPlaceholder,
13
+ ListPlaceholder,
14
+ } from "../../components/placeholders/placeholders";
15
+ import type { Agent, Office } from "../../real_estate.type";
16
+
17
+ type Layout = "grid" | "list";
18
+
19
+ interface AgentTabContentProps {
20
+ office: Office;
21
+ }
22
+
23
+ export const AgentTabPanel = ({ office }: AgentTabContentProps) => {
24
+ const intl = useIntl();
25
+ const [layout, setLayout] = useState<Layout>("grid");
26
+ const [query, setQuery] = useState<string>("");
27
+ const [sort, setSort] = useState<string>("");
28
+
29
+ const toggleLayout = () => {
30
+ setLayout(layout === "grid" ? "list" : "grid");
31
+ };
32
+
33
+ const {
34
+ data: agentItems,
35
+ hasNextPage,
36
+ isLoading,
37
+ fetchNextPage,
38
+ isFetchingNextPage,
39
+ isError,
40
+ } = useInfiniteQuery({
41
+ queryKey: ["agents", query, sort, office],
42
+ queryFn: async ({ pageParam }: { pageParam: string | undefined }) => {
43
+ return fetchAgents(office, query, pageParam, sort);
44
+ },
45
+ getNextPageParam: (lastPage) => lastPage?.continuation,
46
+ initialPageParam: undefined,
47
+ });
48
+
49
+ const agents = agentItems?.pages?.flatMap((page) => page.agents) || [];
50
+ const navigate = useNavigate();
51
+ const handleAgentClick = (item: Agent) => {
52
+ navigate(`/details/agent`, { state: { office, agent: item } });
53
+ };
54
+
55
+ return (
56
+ <Scrollable>
57
+ <InfiniteScroll
58
+ loadMore={() => fetchNextPage()}
59
+ hasMore={hasNextPage}
60
+ useWindow={false}
61
+ >
62
+ <Box height="full">
63
+ <Rows spacing="2u">
64
+ {((!isError && !isLoading && agents.length > 0) ||
65
+ query?.length) && (
66
+ <AgentSearchFilters
67
+ query={query}
68
+ onQueryChange={setQuery}
69
+ layout={layout}
70
+ onLayoutToggle={toggleLayout}
71
+ sort={sort}
72
+ onSortChange={setSort}
73
+ />
74
+ )}
75
+ {isError && (
76
+ <Text tone="critical">
77
+ {intl.formatMessage({
78
+ defaultMessage: "Error loading agents. Please try again.",
79
+ description: "Error message when agents fail to load",
80
+ })}
81
+ </Text>
82
+ )}
83
+ {isLoading && (
84
+ <Box width="full" paddingBottom="2u">
85
+ {layout === "grid" ? <GridPlaceholder /> : <ListPlaceholder />}
86
+ </Box>
87
+ )}
88
+ {!isError && !isLoading && agents.length === 0 && (
89
+ <Box
90
+ height="full"
91
+ display="flex"
92
+ alignItems="center"
93
+ justifyContent="center"
94
+ >
95
+ <Text>
96
+ {intl.formatMessage({
97
+ defaultMessage: "No agents found in this office.",
98
+ description: "Message shown when no agents are found",
99
+ })}
100
+ </Text>
101
+ </Box>
102
+ )}
103
+ {!isError && !isLoading && agents.length > 0 && (
104
+ <Rows spacing={layout === "grid" ? "2u" : "0"}>
105
+ {layout === "grid" ? (
106
+ <AgentGrid agents={agents} onAgentClick={handleAgentClick} />
107
+ ) : (
108
+ <AgentList agents={agents} onAgentClick={handleAgentClick} />
109
+ )}
110
+ {isFetchingNextPage && (
111
+ <Box width="full" paddingBottom="2u">
112
+ {layout === "grid" ? (
113
+ <GridPlaceholder />
114
+ ) : (
115
+ <ListPlaceholder />
116
+ )}
117
+ </Box>
118
+ )}
119
+ </Rows>
120
+ )}
121
+ </Rows>
122
+ </Box>
123
+ </InfiniteScroll>
124
+ </Scrollable>
125
+ );
126
+ };
@@ -0,0 +1,67 @@
1
+ import {
2
+ Box,
3
+ Tab,
4
+ TabList,
5
+ TabPanel,
6
+ TabPanels,
7
+ Tabs,
8
+ } from "@canva/app-ui-kit";
9
+ import { useIntl } from "react-intl";
10
+ import { useLocation, useNavigate, useParams } from "react-router-dom";
11
+ import { Breadcrumb } from "../../components/breadcrumb/breadcrumb";
12
+ import type { Office } from "../../real_estate.type";
13
+ import { AgentTabPanel } from "./agent_tab_panel";
14
+ import { ListingTabPanel } from "./listing_tab_panel";
15
+
16
+ export const ListPage = () => {
17
+ const navigate = useNavigate();
18
+ const { tab = "listings" } = useParams<{
19
+ tab?: string;
20
+ }>();
21
+ const { office } = useLocation().state as { office: Office };
22
+ const intl = useIntl();
23
+
24
+ return (
25
+ <Tabs
26
+ onSelect={(value) => navigate(`/list/${value}`, { state: { office } })}
27
+ activeId={tab}
28
+ height="fill"
29
+ >
30
+ <Box height="full" display="flex" flexDirection="column" paddingTop="2u">
31
+ <Breadcrumb />
32
+ <Box paddingBottom="1u">
33
+ <TabList>
34
+ <Tab
35
+ id="listings"
36
+ active={tab === "listings"}
37
+ onClick={() => navigate(`/list/listings`, { state: { office } })}
38
+ >
39
+ {intl.formatMessage({
40
+ defaultMessage: "Listings",
41
+ description: "Tab label for property listings",
42
+ })}
43
+ </Tab>
44
+ <Tab
45
+ id="agents"
46
+ active={tab === "agents"}
47
+ onClick={() => navigate(`/list/agents`, { state: { office } })}
48
+ >
49
+ {intl.formatMessage({
50
+ defaultMessage: "Agents",
51
+ description: "Tab label for real estate agents",
52
+ })}
53
+ </Tab>
54
+ </TabList>
55
+ </Box>
56
+ <TabPanels>
57
+ <TabPanel id="listings">
58
+ <ListingTabPanel office={office} />
59
+ </TabPanel>
60
+ <TabPanel id="agents">
61
+ <AgentTabPanel office={office} />
62
+ </TabPanel>
63
+ </TabPanels>
64
+ </Box>
65
+ </Tabs>
66
+ );
67
+ };
@@ -0,0 +1,135 @@
1
+ import { Box, Rows, Scrollable, Text } from "@canva/app-ui-kit";
2
+ import { useInfiniteQuery } from "@tanstack/react-query";
3
+ import { useState } from "react";
4
+ import InfiniteScroll from "react-infinite-scroller";
5
+ import { useIntl } from "react-intl";
6
+ import { useNavigate } from "react-router-dom";
7
+ import { fetchListings } from "../../adapter";
8
+ import { ListingGrid } from "../../components/listing/listing_grid";
9
+ import { ListingList } from "../../components/listing/listing_list";
10
+ import { ListingSearchFilters } from "../../components/listing/listing_search_filters";
11
+ import {
12
+ GridPlaceholder,
13
+ ListPlaceholder,
14
+ } from "../../components/placeholders/placeholders";
15
+ import type { Office, Property } from "../../real_estate.type";
16
+
17
+ type Layout = "grid" | "list";
18
+
19
+ interface ListingTabContentProps {
20
+ office: Office;
21
+ }
22
+
23
+ export const ListingTabPanel = ({ office }: ListingTabContentProps) => {
24
+ const intl = useIntl();
25
+ const [layout, setLayout] = useState<Layout>("grid");
26
+ const [query, setQuery] = useState<string>("");
27
+ const [propertyType, setPropertyType] = useState<string>("");
28
+ const [sort, setSort] = useState<string>("");
29
+
30
+ const toggleLayout = () => {
31
+ setLayout(layout === "grid" ? "list" : "grid");
32
+ };
33
+
34
+ const {
35
+ data: listingItems,
36
+ hasNextPage,
37
+ isLoading,
38
+ fetchNextPage,
39
+ isFetchingNextPage,
40
+ isError,
41
+ } = useInfiniteQuery({
42
+ queryKey: ["listings", query, propertyType, sort, office],
43
+ queryFn: async ({ pageParam }: { pageParam: string | undefined }) => {
44
+ return fetchListings(office, query, propertyType, sort, pageParam);
45
+ },
46
+ getNextPageParam: (lastPage) => lastPage?.continuation,
47
+ initialPageParam: undefined,
48
+ });
49
+ const navigate = useNavigate();
50
+
51
+ const listings = listingItems?.pages?.flatMap((page) => page.listings) || [];
52
+ const handleListingClick = (item: Property) => {
53
+ navigate(`/details/listing`, { state: { office, listing: item } });
54
+ };
55
+
56
+ return (
57
+ <Scrollable>
58
+ <InfiniteScroll
59
+ loadMore={() => fetchNextPage()}
60
+ hasMore={hasNextPage}
61
+ useWindow={false}
62
+ >
63
+ <Box height="full">
64
+ <Rows spacing="2u">
65
+ {((!isError && !isLoading && !!listings.length) ||
66
+ !!query?.length) && (
67
+ <ListingSearchFilters
68
+ query={query}
69
+ onQueryChange={setQuery}
70
+ layout={layout}
71
+ onLayoutToggle={toggleLayout}
72
+ propertyType={propertyType}
73
+ onPropertyTypeChange={setPropertyType}
74
+ sort={sort}
75
+ onSortChange={setSort}
76
+ />
77
+ )}
78
+ {isError && (
79
+ <Text tone="critical">
80
+ {intl.formatMessage({
81
+ defaultMessage: "Error loading listings. Please try again.",
82
+ description: "Error message when listings fail to load",
83
+ })}
84
+ </Text>
85
+ )}
86
+ {isLoading && (
87
+ <Box width="full" paddingBottom="2u">
88
+ {layout === "grid" ? <GridPlaceholder /> : <ListPlaceholder />}
89
+ </Box>
90
+ )}
91
+ {!isError && !isLoading && !listings.length && (
92
+ <Box
93
+ height="full"
94
+ display="flex"
95
+ alignItems="center"
96
+ justifyContent="center"
97
+ >
98
+ <Text>
99
+ {intl.formatMessage({
100
+ defaultMessage: "No listings found.",
101
+ description: "Message shown when no listings are found",
102
+ })}
103
+ </Text>
104
+ </Box>
105
+ )}
106
+ {!isError && !isLoading && !!listings.length && (
107
+ <Rows spacing={layout === "grid" ? "2u" : "0"}>
108
+ {layout === "grid" ? (
109
+ <ListingGrid
110
+ listings={listings}
111
+ onListingClick={handleListingClick}
112
+ />
113
+ ) : (
114
+ <ListingList
115
+ listings={listings}
116
+ onListingClick={handleListingClick}
117
+ />
118
+ )}
119
+ {isFetchingNextPage && (
120
+ <Box width="full" paddingBottom="2u">
121
+ {layout === "grid" ? (
122
+ <GridPlaceholder />
123
+ ) : (
124
+ <ListPlaceholder />
125
+ )}
126
+ </Box>
127
+ )}
128
+ </Rows>
129
+ )}
130
+ </Rows>
131
+ </Box>
132
+ </InfiniteScroll>
133
+ </Scrollable>
134
+ );
135
+ };