mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-26 07:38:33 -06:00
moving v2 to top level
This commit is contained in:
23
src/services/utils/htmlIsCloseEnough.test.ts
Normal file
23
src/services/utils/htmlIsCloseEnough.test.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { removeHtmlDetails } from "./htmlIsCloseEnough";
|
||||
|
||||
describe("html can be close enough", () => {
|
||||
it("scenario 1", () => {
|
||||
const localHTML = `<h2>Local Development Environment</h2><p>Integrate your api </p><ol><li>Set up a docker compose file for local development inside your react frontend git repo.<ul><li>Have two services, one for the client and the other for the api</li><li>Reference the <code>:latest</code> image for your api container</li></ul></li><li>Be sure to mount your react client code as a volume to enable hot reloading</li><li>Configure vite to proxy <code>/api</code> requests to the api container</li><li>Your api should still persist the data, even if the container gets re-created (use a volume)</li></ol><h2>Calling Your API from React</h2><p>Create an <code>apiService.ts</code> file to store all functions that make api calls. Have your inventory item context load your items from the api on startup. Use the api calls for all CRUD operations on your app. When modifying the list of items, ensure the item is modified on the api before you show the modified value on the client.</p><h2>Submission</h2><p>Submit screenshots of code relating to each rubric item. Include 1+ questions you would like to ask a the beginning of class</p>`;
|
||||
const canvasHTML = `<link rel="stylesheet" href="https://instructure-uploads-2.s3.amazonaws.com/account_20000000000010/attachments/162497727/DesignPlus%20Mobile%20%25282024%20May%2013%2529.css"><h2>Local Development Environment</h2><p>Integrate your api </p><ol><li>Set up a docker compose file for local development inside your react frontend git repo.<ul><li>Have two services, one for the client and the other for the api</li><li>Reference the <code>:latest</code> image for your api container</li></ul></li><li>Be sure to mount your react client code as a volume to enable hot reloading</li><li>Configure vite to proxy <code>/api</code> requests to the api container</li><li>Your api should still persist the data, even if the container gets re-created (use a volume)</li></ol><h2>Calling Your API from React</h2><p>Create an <code>apiService.ts</code> file to store all functions that make api calls. Have your inventory item context load your items from the api on startup. Use the api calls for all CRUD operations on your app. When modifying the list of items, ensure the item is modified on the api before you show the modified value on the client.</p><h2>Submission</h2><p>Submit screenshots of code relating to each rubric item. Include 1+ questions you would like to ask a the beginning of class</p><script src="https://instructure-uploads-2.s3.amazonaws.com/account_20000000000010/attachments/162497726/DesignPlus%20Mobile%20Java%20%25282024%20May%2013%2529.js"></script>`;
|
||||
|
||||
expect(removeHtmlDetails(localHTML)).toBe(removeHtmlDetails(canvasHTML));
|
||||
});
|
||||
it("scenario 2", () => {
|
||||
const localHTML = `<p>the changesasdf</p>`;
|
||||
const canvasHTML = `<link rel="stylesheet" href="https://instructure-uploads-2.s3.amazonaws.com/account_20000000000010/attachments/162497727/DesignPlus%20Mobile%20%25282024%20May%2013%2529.css"><p>the changes\nasdf</p>\n<h2>Local Development Environment</h2>\n<p>Integrate your api </p>\n<ol>\n<li>Set up a docker compose file for local development inside your react frontend git repo.<ul>\n<li>Have two services, one for the client and the other for the api</li>\n<li>Reference the <code>:latest</code> image for your api container</li>\n</ul>\n</li>\n<li>Be sure to mount your react client code as a volume to enable hot reloading</li>\n<li>Configure vite to proxy <code>/api</code> requests to the api container</li>\n<li>Your api should still persist the data, even if the container gets re-created (use a volume)</li>\n</ol>\n<h2>Calling Your API from React</h2>\n<p>Create an <code>apiService.ts</code> file to store all functions that make api calls. Have your inventory item context load your items from the api on startup. Use the api calls for all CRUD operations on your app. When modifying the list of items, ensure the item is modified on the api before you show the modified value on the client.</p>\n<h2>Submission</h2>\n<p>Submit screenshots of code relating to each rubric item. Include 1+ questions you would like to ask a the beginning of class</p>\n<script src="https://instructure-uploads-2.s3.amazonaws.com/account_20000000000010/attachments/162497726/DesignPlus%20Mobile%20Java%20%25282024%20May%2013%2529.js"></script>"`;
|
||||
|
||||
expect(removeHtmlDetails(localHTML)).not.toBe(removeHtmlDetails(canvasHTML));
|
||||
});
|
||||
it("scenario 3", () => {
|
||||
const localHTML = `<p>Create a custom component to properly style, format, and validate text input elements. Replace all text inputs in your list detail app with this new text input component. You may write addition custom hooks to add functionality. This component should include the "supporting" html elements to the input, like the label and the feedback.</p><p>Requirements:</p><ol><li>Have a way for have the parent specify validation rules for the input<ul><li>Support at least required, min/max length, regex pattern</li></ul></li><li>The parent can set and read the data from the input</li><li>The component looks good in any layout (e.g. looks good for any width given to it by the parent)</li><li>The component informs the parent if the value is valid or invalid</li><li>The component visually indicates if the value is valid or invalid after the input has been touched by the user.</li></ol><p>Push your code to the same repo</p><p>Submit a text entry about how it went and a question you would like to bring up with the class. Submit a screenshot of your re-usable input component's code and your component running on your webpage.</p><h2>Extra Credit</h2><p>Add debouncing to your validation. Indicate in your submission if you did this.</p>`
|
||||
const canvasHTML = "\u003clink rel=\"stylesheet\" href=\"https://instructure-uploads-2.s3.amazonaws.com/account_20000000000010/attachments/162497727/DesignPlus%20Mobile%20%25282024%20May%2013%2529.css\"\u003e\u003cp\u003eCreate a custom component to properly style, format, and validate text input elements. Replace all text inputs in your list detail app with this new text input component. You may write addition custom hooks to add functionality. This component should include the \"supporting\" html elements to the input, like the label and the feedback.\u003c/p\u003e\n\u003cp\u003eRequirements:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eHave a way for have the parent specify validation rules for the input\n\u003cul\u003e\n\u003cli\u003eSupport at least required, min/max length, regex pattern\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eThe parent can set and read the data from the input\u003c/li\u003e\n\u003cli\u003eThe component looks good in any layout (e.g. looks good for any width given to it by the parent)\u003c/li\u003e\n\u003cli\u003eThe component informs the parent if the value is valid or invalid\u003c/li\u003e\n\u003cli\u003eThe component visually indicates if the value is valid or invalid after the input has been touched by the user.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003ePush your code to the same repo\u003c/p\u003e\n\u003cp\u003eSubmit a text entry about how it went and a question you would like to bring up with the class. Submit a screenshot of your re-usable input component's code and your component running on your webpage.\u003c/p\u003e\n\u003ch2 id=\"extra-credit\"\u003eExtra Credit\u003c/h2\u003e\n\u003cp\u003eAdd debouncing to your validation. Indicate in your submission if you did this.\u003c/p\u003e\n\u003cscript src=\"https://instructure-uploads-2.s3.amazonaws.com/account_20000000000010/attachments/162497726/DesignPlus%20Mobile%20Java%20%25282024%20May%2013%2529.js\"\u003e\u003c/script\u003e";
|
||||
|
||||
expect(removeHtmlDetails(localHTML)).toBe(removeHtmlDetails(canvasHTML));
|
||||
});
|
||||
});
|
||||
36
src/services/utils/htmlIsCloseEnough.ts
Normal file
36
src/services/utils/htmlIsCloseEnough.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
const scriptRegex = /<script.*?<\/script>/g;
|
||||
const linkTagRegex = /<link\s+rel="[^"]*"\s+href="[^"]*"[^>]*>/g;
|
||||
const htmlAttribute = /\s+\w+="[^"]*"|\s+\w+='[^']*'|\s+\w+=[^\s>]+/g;
|
||||
|
||||
function replaceUnicodeEscapes(input: string) {
|
||||
return input.replace(/\\u[\dA-Fa-f]{4}/g, (match) => {
|
||||
// Extract the hexadecimal part and convert it to a character
|
||||
return String.fromCharCode(parseInt(match.slice(2), 16));
|
||||
});
|
||||
}
|
||||
|
||||
export const removeHtmlDetails = (html: string) => {
|
||||
const withoutUnicode = replaceUnicodeEscapes(html);
|
||||
return withoutUnicode
|
||||
.replaceAll(scriptRegex, "")
|
||||
.replaceAll(linkTagRegex, "")
|
||||
.replaceAll(htmlAttribute, "")
|
||||
.replaceAll(/\\"/g, '"')
|
||||
.replaceAll(/\s/g, "")
|
||||
.replaceAll(/<hr\s*\/?>/g, "<hr>")
|
||||
.replaceAll(/<br\s*\/?>/g, "<br>")
|
||||
.replaceAll(/>/g, "")
|
||||
.replaceAll(/</g, "")
|
||||
.replaceAll(/>/g, "")
|
||||
.replaceAll(/</g, "")
|
||||
.replaceAll(/"/g, "")
|
||||
.replaceAll(/"/g, "")
|
||||
.replaceAll(/&/g, "")
|
||||
.replaceAll(/&/g, "");
|
||||
};
|
||||
|
||||
export const htmlIsCloseEnough = (html1: string, html2: string) => {
|
||||
const simple1 = removeHtmlDetails(html1);
|
||||
const simple2 = removeHtmlDetails(html2);
|
||||
return simple1 === simple2;
|
||||
};
|
||||
124
src/services/utils/queryClient.tsx
Normal file
124
src/services/utils/queryClient.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import toast, { ErrorIcon, CheckmarkIcon } from "react-hot-toast";
|
||||
import { ReactNode } from "react";
|
||||
import { MutationCache, QueryCache, QueryClient } from "@tanstack/react-query";
|
||||
|
||||
const addErrorAsToast = async (error: any) => {
|
||||
console.error("error from toast", error);
|
||||
const message = getErrorMessage(error);
|
||||
|
||||
toast(
|
||||
(t: any) => (
|
||||
<div className="row">
|
||||
<div className="col-auto my-auto">
|
||||
<ErrorIcon />
|
||||
</div>
|
||||
<div className="col my-auto">
|
||||
<div className="white-space">{message}</div>
|
||||
<div>
|
||||
<a
|
||||
href="https://snow.kualibuild.com/app/651eeebc32976c013a4c4739/run"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Report Bug
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-auto my-auto">
|
||||
<button
|
||||
onClick={() => toast.dismiss(t.id)}
|
||||
className="btn btn-outline-secondary btn-sm"
|
||||
>
|
||||
<i className="bi bi-x"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
duration: Infinity,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export function getErrorMessage(error: any) {
|
||||
if (error?.response?.status === 422) {
|
||||
console.log(error.response.data.detail);
|
||||
const serializationMessages = error.response.data.detail.map(
|
||||
(d: any) => `${d.type} - ${d.loc[1]}`
|
||||
);
|
||||
return `Deserialization error on request:\n${serializationMessages.join(
|
||||
"\n"
|
||||
)}`;
|
||||
}
|
||||
if (typeof error === "string") {
|
||||
return error;
|
||||
}
|
||||
if (error.response?.data.detail) {
|
||||
if (typeof error.response?.data.detail === "string") {
|
||||
return error.response?.data.detail;
|
||||
} else return JSON.stringify(error.response?.data.detail);
|
||||
}
|
||||
console.log(error);
|
||||
return "Error With Request";
|
||||
}
|
||||
|
||||
export function createInfoToast(
|
||||
children: ReactNode,
|
||||
onClose: () => void = () => {}
|
||||
) {
|
||||
const closeHandler = (t: any) => {
|
||||
toast.dismiss(t.id);
|
||||
onClose();
|
||||
};
|
||||
toast(
|
||||
(t: any) => (
|
||||
<div className="row">
|
||||
<div className="col-auto my-auto">
|
||||
<i className="bi bi-info-circle-fill"></i>
|
||||
</div>
|
||||
<div className="col my-auto">{children}</div>
|
||||
<div className="col-auto my-auto">
|
||||
<button
|
||||
onClick={() => closeHandler(t)}
|
||||
className="btn btn-outline-secondary py-1"
|
||||
>
|
||||
<i className="bi-x-lg" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
duration: Infinity,
|
||||
style: {
|
||||
maxWidth: "75em",
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function createSuccessToast(message: string) {
|
||||
toast(
|
||||
(t: any) => (
|
||||
<div className="row">
|
||||
<div className="col-auto my-auto">
|
||||
<CheckmarkIcon />
|
||||
</div>
|
||||
<div className="col my-auto"> {message}</div>
|
||||
<div className="col-auto my-auto">
|
||||
<button
|
||||
onClick={() => toast.dismiss(t.id)}
|
||||
className="btn btn-outline-secondary py-1"
|
||||
>
|
||||
<i className="bi-x-lg" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
duration: Infinity,
|
||||
style: {
|
||||
maxWidth: "75em",
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user