mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
Merge branch 'rc' of https://github.com/rowyio/rowy into rc
This commit is contained in:
17
README.md
17
README.md
@@ -9,7 +9,7 @@
|
||||
Build on Google Cloud Platform in minutes
|
||||
</h3>
|
||||
<p align="center">
|
||||
Manage Firestore data in a spreadsheet-like UI, write Cloud Functions effortlessly in the browser, and connect to your favorite third party platforms such as SendGrid, Twilio, Algolia, sSack and more.
|
||||
Manage Firestore data in a spreadsheet-like UI, write Cloud Functions effortlessly in the browser, and connect to your favorite third party platforms such as SendGrid, Twilio, Algolia, Slack and more.
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
@@ -31,7 +31,10 @@ Manage Firestore data in a spreadsheet-like UI, write Cloud Functions effortless
|
||||
|
||||
<img width="100%" src="https://firebasestorage.googleapis.com/v0/b/rowyio.appspot.com/o/publicDemo%2FRowy%20Website%20Video%20GIF%20Small.gif?alt=media&token=3f699a8f-c1f2-4046-8ed5-e4ff66947cd8" />
|
||||
|
||||
## Quick try
|
||||
## Live Demo
|
||||
💥 Check out the [live demo](https://demo.rowy.io/) of Rowy 💥
|
||||
|
||||
## Quick Deploy
|
||||
|
||||
Set up Rowy on your Google Cloud Platform project with this one-click deploy
|
||||
button.
|
||||
@@ -53,7 +56,7 @@ You can find the full documentation with how-to guides
|
||||
- Multiple views for the same collection
|
||||
- Bulk import or export data - csv, json
|
||||
|
||||
### Supercharge your database with cloud functions and ready made extension
|
||||
### Automate with cloud functions and ready made extensions
|
||||
|
||||
- Effortlessly build cloud functions on field level triggers right in the browser
|
||||
- Use any NPM modules or APIs
|
||||
@@ -82,7 +85,7 @@ You can find the full documentation with how-to guides
|
||||
|
||||
## Install
|
||||
|
||||
Set up Rowy on your Google Cloud project with this one-click deploy button.
|
||||
Set up Rowy on your Google Cloud project with this one-click deploy button. Your data and cloud functions stay on your own Firestore/GCP.
|
||||
|
||||
[](https://deploy.cloud.run/?git_repo=https://github.com/rowyio/rowyRun.git)
|
||||
|
||||
@@ -96,6 +99,12 @@ access to your service account or any of your data.
|
||||
Alternatively, you can manually install by
|
||||
[following this guide](https://docs.rowy.io/setup/install).
|
||||
|
||||
## Roadmap
|
||||
|
||||
[View our roadmap](https://demo.rowy.io/table/roadmap) on Rowy - Upvote, downvote, share your thoughts!
|
||||
|
||||
If you'd like to propose a feature, submit an issue [here](https://github.com/rowyio/rowy/issues/new?assignees=&labels=&template=feature_request.md&title=).
|
||||
|
||||
## Join our community
|
||||
|
||||
We would love to connect and hear what you think!
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Rowy",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.1",
|
||||
"homepage": "https://rowy.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -64,7 +64,7 @@
|
||||
"react-usestateref": "^1.0.5",
|
||||
"serve": "^11.3.2",
|
||||
"swr": "^1.0.1",
|
||||
"tinymce": "^5.9.2",
|
||||
"tinymce": "^5.10.0",
|
||||
"typescript": "^4.4.2",
|
||||
"use-algolia": "^1.4.1",
|
||||
"use-debounce": "^3.3.0",
|
||||
|
||||
5
src/atoms/Table.ts
Normal file
5
src/atoms/Table.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { atomWithHash } from "jotai/utils";
|
||||
|
||||
export const modalAtom = atomWithHash<
|
||||
"cloudLogs" | "extensions" | "webhooks" | "export" | ""
|
||||
>("modal", "");
|
||||
@@ -15,7 +15,8 @@ import CircularProgressOptical from "@src/components/CircularProgressOptical";
|
||||
import { isTargetInsideBox } from "utils/fns";
|
||||
import { useSnackLogContext } from "@src/contexts/SnackLogContext";
|
||||
import useBuildLogs from "./useBuildLogs";
|
||||
import { modalAtom, cloudLogFiltersAtom } from "../utils";
|
||||
import { modalAtom } from "@src/atoms/Table";
|
||||
import { cloudLogFiltersAtom } from "../utils";
|
||||
|
||||
export interface IBuildLogsSnackProps {
|
||||
onClose: () => void;
|
||||
|
||||
@@ -189,6 +189,16 @@ export default function CloudLogItem({
|
||||
<Typography variant="inherit" noWrap className="log-preview">
|
||||
{data.payload === "textPayload" && data.textPayload}
|
||||
{_get(data, "httpRequest.requestUrl")?.split(".run.app").pop()}
|
||||
{data.payload === "jsonPayload" && (
|
||||
<Typography
|
||||
variant="inherit"
|
||||
color="error"
|
||||
fontWeight="bold"
|
||||
component="span"
|
||||
>
|
||||
{data.jsonPayload.error}{" "}
|
||||
</Typography>
|
||||
)}
|
||||
{data.payload === "jsonPayload" &&
|
||||
stringify(data.jsonPayload.body ?? data.jsonPayload, {
|
||||
space: 2,
|
||||
@@ -203,16 +213,29 @@ export default function CloudLogItem({
|
||||
</Typography>
|
||||
)}
|
||||
{data.payload === "jsonPayload" && (
|
||||
<ReactJson
|
||||
src={data.jsonPayload.body ?? data.jsonPayload}
|
||||
name={data.jsonPayload.body ? "body" : "jsonPayload"}
|
||||
theme={theme.palette.mode === "dark" ? "monokai" : "rjv-default"}
|
||||
iconStyle="triangle"
|
||||
style={{ font: "inherit", backgroundColor: "transparent" }}
|
||||
displayDataTypes={false}
|
||||
quotesOnKeys={false}
|
||||
sortKeys
|
||||
/>
|
||||
<>
|
||||
{data.payload === "jsonPayload" && data.jsonPayload.error && (
|
||||
<Typography
|
||||
variant="inherit"
|
||||
color="error"
|
||||
fontWeight="bold"
|
||||
paragraph
|
||||
style={{ whiteSpace: "pre-wrap" }}
|
||||
>
|
||||
{data.jsonPayload.error}
|
||||
</Typography>
|
||||
)}
|
||||
<ReactJson
|
||||
src={data.jsonPayload.body ?? data.jsonPayload}
|
||||
name={data.jsonPayload.body ? "body" : "jsonPayload"}
|
||||
theme={theme.palette.mode === "dark" ? "monokai" : "rjv-default"}
|
||||
iconStyle="triangle"
|
||||
style={{ font: "inherit", backgroundColor: "transparent" }}
|
||||
displayDataTypes={false}
|
||||
quotesOnKeys={false}
|
||||
sortKeys
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{data.payload && <Divider sx={{ my: 1 }} />}
|
||||
|
||||
@@ -4,11 +4,9 @@ import TableHeaderButton from "../TableHeaderButton";
|
||||
import LogsIcon from "@src/assets/icons/CloudLogs";
|
||||
import CloudLogsModal from "./CloudLogsModal";
|
||||
|
||||
import { modalAtom } from "./utils";
|
||||
import { modalAtom } from "@src/atoms/Table";
|
||||
|
||||
export interface ICloudLogsProps {}
|
||||
|
||||
export default function CloudLogs(props: ICloudLogsProps) {
|
||||
export default function CloudLogs() {
|
||||
const [modal, setModal] = useAtom(modalAtom);
|
||||
const open = modal === "cloudLogs";
|
||||
const setOpen = (open: boolean) => setModal(open ? "cloudLogs" : "");
|
||||
|
||||
@@ -3,8 +3,6 @@ import { sub } from "date-fns";
|
||||
|
||||
import type { IProjectContext } from "@src/contexts/ProjectContext";
|
||||
|
||||
export const modalAtom = atomWithHash<"cloudLogs" | "">("modal", "");
|
||||
|
||||
export type CloudLogFilters = {
|
||||
type: "webhook" | "audit" | "build";
|
||||
timeRange:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useMemo } from "react";
|
||||
import { useAtom } from "jotai";
|
||||
|
||||
import { makeStyles, createStyles } from "@mui/styles";
|
||||
import { DialogContent, Tab, Divider } from "@mui/material";
|
||||
@@ -14,8 +15,9 @@ import ExportDetails from "./Export";
|
||||
import DownloadDetails from "./Download";
|
||||
|
||||
import { useProjectContext } from "@src/contexts/ProjectContext";
|
||||
import { db } from "../../../firebase";
|
||||
import { db } from "@src/firebase";
|
||||
import { isCollectionGroup } from "@src/utils/fns";
|
||||
import { modalAtom } from "@src/atoms/Table";
|
||||
|
||||
const useStyles = makeStyles((theme) =>
|
||||
createStyles({
|
||||
@@ -47,7 +49,11 @@ const useStyles = makeStyles((theme) =>
|
||||
|
||||
export default function Export() {
|
||||
const classes = useStyles();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const [modal, setModal] = useAtom(modalAtom);
|
||||
const open = modal === "export";
|
||||
const setOpen = (open: boolean) => setModal(open ? "export" : "");
|
||||
|
||||
const [mode, setMode] = useState<"Export" | "Download">("Export");
|
||||
const { tableState } = useProjectContext();
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import { useAtom } from "jotai";
|
||||
import _isEqual from "lodash/isEqual";
|
||||
|
||||
import TableHeaderButton from "../TableHeaderButton";
|
||||
@@ -17,6 +18,7 @@ import { useSnackLogContext } from "@src/contexts/SnackLogContext";
|
||||
import { emptyExtensionObject, IExtension, ExtensionType } from "./utils";
|
||||
import { runRoutes } from "@src/constants/runRoutes";
|
||||
import { analytics } from "@src/analytics";
|
||||
import { modalAtom } from "@src/atoms/Table";
|
||||
|
||||
export default function Extensions() {
|
||||
const { tableState, tableActions, rowyRun } = useProjectContext();
|
||||
@@ -28,7 +30,11 @@ export default function Extensions() {
|
||||
const [localExtensionsObjects, setLocalExtensionsObjects] = useState(
|
||||
currentExtensionObjects
|
||||
);
|
||||
const [openExtensionList, setOpenExtensionList] = useState(false);
|
||||
|
||||
const [modal, setModal] = useAtom(modalAtom);
|
||||
const open = modal === "extensions";
|
||||
const setOpen = (open: boolean) => setModal(open ? "extensions" : "");
|
||||
|
||||
const [openMigrationGuide, setOpenMigrationGuide] = useState(false);
|
||||
const [extensionModal, setExtensionModal] = useState<{
|
||||
mode: "add" | "update";
|
||||
@@ -45,26 +51,26 @@ export default function Extensions() {
|
||||
console.log("Extension migration required.");
|
||||
setOpenMigrationGuide(true);
|
||||
} else {
|
||||
setOpenExtensionList(true);
|
||||
setOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = (
|
||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
_setOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
) => {
|
||||
if (edited) {
|
||||
setOpen(true);
|
||||
_setOpen(true);
|
||||
requestConfirmation({
|
||||
title: "Discard changes?",
|
||||
confirm: "Discard",
|
||||
handleConfirm: () => {
|
||||
setOpen(false);
|
||||
_setOpen(false);
|
||||
setLocalExtensionsObjects(currentExtensionObjects);
|
||||
setOpenExtensionList(false);
|
||||
setOpen(false);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
setOpenExtensionList(false);
|
||||
setOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -73,7 +79,7 @@ export default function Extensions() {
|
||||
"extensionObjects",
|
||||
localExtensionsObjects
|
||||
);
|
||||
setOpenExtensionList(false);
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const handleSaveDeploy = async () => {
|
||||
@@ -189,7 +195,7 @@ export default function Extensions() {
|
||||
icon={<ExtensionIcon />}
|
||||
/>
|
||||
|
||||
{openExtensionList && !!tableState && (
|
||||
{open && !!tableState && (
|
||||
<Modal
|
||||
onClose={handleClose}
|
||||
disableBackdropClick={edited}
|
||||
@@ -236,9 +242,7 @@ export default function Extensions() {
|
||||
|
||||
{extensionModal && (
|
||||
<ExtensionModal
|
||||
handleClose={() => {
|
||||
setExtensionModal(null);
|
||||
}}
|
||||
handleClose={() => setExtensionModal(null)}
|
||||
handleAdd={handleAddExtension}
|
||||
handleUpdate={handleUpdateExtension}
|
||||
mode={extensionModal.mode}
|
||||
@@ -248,12 +252,10 @@ export default function Extensions() {
|
||||
|
||||
{openMigrationGuide && (
|
||||
<ExtensionMigration
|
||||
handleClose={() => {
|
||||
setOpenMigrationGuide(false);
|
||||
}}
|
||||
handleClose={() => setOpenMigrationGuide(false)}
|
||||
handleUpgradeComplete={() => {
|
||||
setOpenMigrationGuide(false);
|
||||
setOpenExtensionList(true);
|
||||
setOpen(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -22,10 +22,8 @@ import EmptyState from "@src/components/EmptyState";
|
||||
import { webhookNames, IWebhook } from "./utils";
|
||||
import { DATE_TIME_FORMAT } from "@src/constants/dates";
|
||||
import { useProjectContext } from "@src/contexts/ProjectContext";
|
||||
import {
|
||||
modalAtom,
|
||||
cloudLogFiltersAtom,
|
||||
} from "@src/components/TableHeader/CloudLogs/utils";
|
||||
import { modalAtom } from "@src/atoms/Table";
|
||||
import { cloudLogFiltersAtom } from "@src/components/TableHeader/CloudLogs/utils";
|
||||
|
||||
export interface IWebhookListProps {
|
||||
webhooks: IWebhook[];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import { useAtom } from "jotai";
|
||||
import _isEqual from "lodash/isEqual";
|
||||
|
||||
import TableHeaderButton from "../TableHeaderButton";
|
||||
@@ -16,6 +17,7 @@ import { emptyWebhookObject, IWebhook, WebhookType } from "./utils";
|
||||
import { runRoutes } from "@src/constants/runRoutes";
|
||||
import { analytics } from "@src/analytics";
|
||||
import { useSnackbar } from "notistack";
|
||||
import { modalAtom } from "@src/atoms/Table";
|
||||
|
||||
export default function Webhooks() {
|
||||
const { tableState, tableActions, rowyRun, compatibleRowyRunVersion } =
|
||||
@@ -27,7 +29,11 @@ export default function Webhooks() {
|
||||
const currentWebhooks = (tableState?.config.webhooks ?? []) as IWebhook[];
|
||||
const [localWebhooksObjects, setLocalWebhooksObjects] =
|
||||
useState(currentWebhooks);
|
||||
const [openWebhookList, setOpenWebhookList] = useState(false);
|
||||
|
||||
const [modal, setModal] = useAtom(modalAtom);
|
||||
const open = modal === "webhooks";
|
||||
const setOpen = (open: boolean) => setModal(open ? "webhooks" : "");
|
||||
|
||||
const [webhookModal, setWebhookModal] = useState<{
|
||||
mode: "add" | "update";
|
||||
webhookObject: IWebhook;
|
||||
@@ -38,32 +44,30 @@ export default function Webhooks() {
|
||||
|
||||
const edited = !_isEqual(currentWebhooks, localWebhooksObjects);
|
||||
|
||||
const handleOpen = () => {
|
||||
setOpenWebhookList(true);
|
||||
};
|
||||
const handleOpen = () => setOpen(true);
|
||||
|
||||
const handleClose = (
|
||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
_setOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
) => {
|
||||
if (edited) {
|
||||
setOpen(true);
|
||||
_setOpen(true);
|
||||
requestConfirmation({
|
||||
title: "Discard changes?",
|
||||
confirm: "Discard",
|
||||
handleConfirm: () => {
|
||||
setOpen(false);
|
||||
_setOpen(false);
|
||||
setLocalWebhooksObjects(currentWebhooks);
|
||||
setOpenWebhookList(false);
|
||||
setOpen(false);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
setOpenWebhookList(false);
|
||||
setOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveWebhooks = async () => {
|
||||
tableActions?.table.updateConfig("webhooks", localWebhooksObjects);
|
||||
setOpenWebhookList(false);
|
||||
setOpen(false);
|
||||
// TODO: convert to async function that awaits for the document write to complete
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
};
|
||||
@@ -169,7 +173,7 @@ export default function Webhooks() {
|
||||
icon={<WebhookIcon />}
|
||||
/>
|
||||
|
||||
{openWebhookList && !!tableState && (
|
||||
{open && !!tableState && (
|
||||
<Modal
|
||||
onClose={handleClose}
|
||||
disableBackdropClick={edited}
|
||||
@@ -215,9 +219,7 @@ export default function Webhooks() {
|
||||
|
||||
{webhookModal && (
|
||||
<WebhookModal
|
||||
handleClose={() => {
|
||||
setWebhookModal(null);
|
||||
}}
|
||||
handleClose={() => setWebhookModal(null)}
|
||||
handleAdd={handleAddWebhook}
|
||||
handleUpdate={handleUpdateWebhook}
|
||||
mode={webhookModal.mode}
|
||||
|
||||
@@ -564,6 +564,12 @@ export const components = (theme: Theme): ThemeOptions => {
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiLoadingButton: {
|
||||
defaultProps: {
|
||||
variant: "outlined",
|
||||
color: "secondary",
|
||||
},
|
||||
},
|
||||
MuiButtonGroup: {
|
||||
styleOverrides: {
|
||||
grouped: { minWidth: 32 },
|
||||
|
||||
@@ -14,7 +14,9 @@ export default function PrivateRoute({ render, ...props }: IPrivateRouteProps) {
|
||||
if (!!currentUser) return <Route {...props} render={render} />;
|
||||
|
||||
const redirect =
|
||||
(props.location?.pathname ?? "") + (props.location?.search ?? "");
|
||||
(props.location?.pathname ?? "") +
|
||||
(props.location?.search ?? "") +
|
||||
(props.location?.hash ?? "");
|
||||
|
||||
if (currentUser === null)
|
||||
return (
|
||||
|
||||
20
yarn.lock
20
yarn.lock
@@ -14568,9 +14568,9 @@ semver-diff@^3.1.1:
|
||||
semver "^6.3.0"
|
||||
|
||||
semver-regex@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.2.tgz#34b4c0d361eef262e07199dbef316d0f2ab11807"
|
||||
integrity sha512-bXWyL6EAKOJa81XG1OZ/Yyuq+oT0b2YLlxx7c+mrdYPaPbnj6WgVULXhinMIeZGufuUBu/eVRqXEhiv4imfwxA==
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.3.tgz#b2bcc6f97f63269f286994e297e229b6245d0dc3"
|
||||
integrity sha512-Aqi54Mk9uYTjVexLnR67rTyBusmwd04cLkHy9hNvk3+G3nT2Oyg7E0l4XVbOaNwIvQ3hHeYxGcyEy+mKreyBFQ==
|
||||
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1:
|
||||
version "5.7.1"
|
||||
@@ -15733,10 +15733,10 @@ tinycolor2@^1.4.1:
|
||||
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803"
|
||||
integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==
|
||||
|
||||
tinymce@^5.5.1, tinymce@^5.9.2:
|
||||
version "5.9.2"
|
||||
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-5.9.2.tgz#c56a1d7800ac23026fbe6e0fcf444c0f157ccafe"
|
||||
integrity sha512-/dHTsbxo0YwLvB5krRqiw/qHEm04/k6l0dvAQ3hO5oNw4e9QalKcUQCdr+g/b/FWcsUMP6scvKmm8MX50/j3Cg==
|
||||
tinymce@^5.10.0, tinymce@^5.5.1:
|
||||
version "5.10.0"
|
||||
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-5.10.0.tgz#5f88b40c2b26dc72b126f7d04944014dcb1ace05"
|
||||
integrity sha512-SaqBK8GtTKYSsTfhKdN0+NrZRgmVWO+j3fvgzLjt0t/g0osNzRH5os8icm2Rv5HvaeTd4TpxetUuLE+R9yg/yg==
|
||||
|
||||
tmp@0.0.33, tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
@@ -15746,9 +15746,9 @@ tmp@0.0.33, tmp@^0.0.33:
|
||||
os-tmpdir "~1.0.2"
|
||||
|
||||
tmpl@1.0.x:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"
|
||||
integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
|
||||
integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==
|
||||
|
||||
to-arraybuffer@^1.0.0:
|
||||
version "1.0.1"
|
||||
|
||||
Reference in New Issue
Block a user