= пре 2 година
родитељ
комит
ef9c9b54a0
9 измењених фајлова са 415 додато и 118 уклоњено
  1. BIN
      .DS_Store
  2. BIN
      src/.DS_Store
  3. 0 6
      src/actions/actionSnippetByOwner.js
  4. 1 1
      src/actions/requests.js
  5. BIN
      src/code.png
  6. 134 64
      src/pages/Main.css
  7. 164 0
      src/pages/Project.js
  8. 61 0
      src/pages/Projects.js
  9. 55 47
      src/pages/Search.js


+ 0 - 6
src/actions/actionSnippetByOwner.js

@@ -1,6 +0,0 @@
-import { actionPromise } from "./actionPromise";
-import { snippetByOwner } from "./requests";
-
-export const actionSnippetFindByOwner = (id) => async (dispatch) => {
-    return await dispatch(actionPromise("findSnippet", snippetByOwner(id)));
-};

+ 1 - 1
src/actions/requests.js

@@ -110,7 +110,7 @@ export const snippetByOwner = async (id) => {
         }
   }`
 
-    let variables = {query: JSON.stringify([{owner: id } , {sort:[{_id: -1}]}])}
+    let variables = {query: JSON.stringify([{___owner: id } , {sort:[{_id: -1}]}])}
   
     let res = gql(query, variables)
     return res


+ 134 - 64
src/pages/Main.css

@@ -1,11 +1,11 @@
 #body {
-    margin: 0;
-    padding: 0;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    min-height: 90vh;
-    font-family: 'Telefon Black', Sans-Serif;
+  margin: 0;
+  padding: 0;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-height: 90vh;
+  font-family: "Telefon Black", Sans-Serif;
 }
 
 .box {
@@ -18,7 +18,7 @@
 }
 
 .box::before {
-  content: '';
+  content: "";
   position: absolute;
   top: -2px;
   left: -2px;
@@ -29,7 +29,7 @@
 }
 
 .box::after {
-  content: '';
+  content: "";
   position: absolute;
   top: -2px;
   left: -2px;
@@ -53,91 +53,161 @@
 }
 
 .btn_form {
-    width: 180px;
-    height: 40px;
-    text-decoration: none;
-    color: #c9b6cf;
-    text-align: center;
-    line-height: 20px;
-    display: block;
-    margin-left: 10px;
-    margin-right: 10px;
-    font: normal 17px arial;
-    cursor: pointer;
+  width: 180px;
+  height: 40px;
+  text-decoration: none;
+  color: #c9b6cf;
+  text-align: center;
+  line-height: 20px;
+  display: block;
+  margin-left: 10px;
+  margin-right: 10px;
+  font: normal 17px arial;
+  cursor: pointer;
 }
 
 .btn_form:not(.active) {
-    box-shadow: inset 0 1px 1px rgba(111, 55, 125, 0.8), inset 0 -1px 0px rgba(63, 59, 113, 0.2), 0 9px 16px 0 rgba(0, 0, 0, 0.3), 0 4px 3px 0 rgba(0, 0, 0, 0.3), 0 0 0 1px #150a1e;
-    background-image: linear-gradient(#3b2751, #271739);
-    text-shadow: 0 0 21px rgba(223, 206, 228, 0.5), 0 -1px 0 #311d47;
+  box-shadow: inset 0 1px 1px rgba(111, 55, 125, 0.8),
+    inset 0 -1px 0px rgba(63, 59, 113, 0.2), 0 9px 16px 0 rgba(0, 0, 0, 0.3),
+    0 4px 3px 0 rgba(0, 0, 0, 0.3), 0 0 0 1px #150a1e;
+  background-image: linear-gradient(#3b2751, #271739);
+  text-shadow: 0 0 21px rgba(223, 206, 228, 0.5), 0 -1px 0 #311d47;
 }
-  
+
 .btn_form:not(.active):hover,
 .btn_form:not(.active):focus {
-    transition: color 200ms linear, text-shadow 500ms linear;
-    color: #fff;
-    text-shadow: 0 0 21px rgba(223, 206, 228, 0.5), 0 0 10px rgba(223, 206, 228, 0.4), 0 0 2px #2a153c;
+  transition: color 200ms linear, text-shadow 500ms linear;
+  color: #fff;
+  text-shadow: 0 0 21px rgba(223, 206, 228, 0.5),
+    0 0 10px rgba(223, 206, 228, 0.4), 0 0 2px #2a153c;
 }
 
 .btn_form:not(:hover) {
-      transition: 0.6s;
+  transition: 0.6s;
 }
 
 .card {
-    width: 500px;
-    height: 360px;
-    padding-left: 100px;
-    margin-left: 470px;
-    margin-top: 100px;
-    padding-top: 10px;
-    background: linear-gradient(#f0e2ef, #faf5f5);
-    -webkit-box-shadow: inset 0 0 40px rgb(36, 16, 61);
-    -moz-box-shadow: inset 0 0 40px rgb(37, 12, 58);
-    box-shadow: inset 0 0 40px rgb(26, 10, 48);
+  width: 500px;
+  height: 360px;
+  padding-left: 100px;
+  margin-left: 470px;
+  margin-top: 100px;
+  padding-top: 10px;
+  background: linear-gradient(#f0e2ef, #faf5f5);
+  -webkit-box-shadow: inset 0 0 40px rgb(36, 16, 61);
+  -moz-box-shadow: inset 0 0 40px rgb(37, 12, 58);
+  box-shadow: inset 0 0 40px rgb(26, 10, 48);
 }
 
 .nick_style {
-    display: block;
-    width: 100px;
-    margin-left: 115px;
+  display: block;
+  width: 100px;
+  margin-left: 115px;
 }
 
 .block_search {
-    display: flex;
-    justify-content: center;
-    align-items: center;
+  display: flex;
+  justify-content: center;
+  align-items: center;
 }
 
 .btn_search {
-    width: 180px;
-    height: 40px;
-    text-decoration: none;
-    color: #b5a2bb;
-    margin-right: 200px;
-    text-align: center;
-    line-height: 20px;
-    font: normal 17px arial;
-    cursor: pointer;
-    margin-bottom: 10px;
+  width: 180px;
+  height: 40px;
+  text-decoration: none;
+  color: #b5a2bb;
+  margin-right: 200px;
+  text-align: center;
+  line-height: 20px;
+  font: normal 17px arial;
+  cursor: pointer;
+  margin-bottom: 10px;
 }
 
 .btn_search:not(.active) {
-    box-shadow: inset 0 1px 1px rgba(111, 55, 125, 0.8), inset 0 -1px 0px rgba(63, 59, 113, 0.2), 0 9px 16px 0 rgba(0, 0, 0, 0.3), 0 4px 3px 0 rgba(0, 0, 0, 0.3), 0 0 0 1px #150a1e;
-    background-image: linear-gradient(#3b2751, #271739);
-    text-shadow: 0 0 21px rgba(223, 206, 228, 0.5), 0 -1px 0 #311d47;
+  box-shadow: inset 0 1px 1px rgba(111, 55, 125, 0.8),
+    inset 0 -1px 0px rgba(63, 59, 113, 0.2), 0 9px 16px 0 rgba(0, 0, 0, 0.3),
+    0 4px 3px 0 rgba(0, 0, 0, 0.3), 0 0 0 1px #150a1e;
+  background-image: linear-gradient(#3b2751, #271739);
+  text-shadow: 0 0 21px rgba(223, 206, 228, 0.5), 0 -1px 0 #311d47;
 }
-  
+
 .btn_search:not(.active):hover,
 .btn_search:not(.active):focus {
-    transition: color 200ms linear, text-shadow 500ms linear;
-    color: #fff;
-    text-shadow: 0 0 21px rgba(223, 206, 228, 0.5), 0 0 10px rgba(223, 206, 228, 0.4), 0 0 2px #2a153c;
+  transition: color 200ms linear, text-shadow 500ms linear;
+  color: #fff;
+  text-shadow: 0 0 21px rgba(223, 206, 228, 0.5),
+    0 0 10px rgba(223, 206, 228, 0.4), 0 0 2px #2a153c;
 }
 
 .btn_search:not(:hover) {
-      transition: 0.6s;
+  transition: 0.6s;
 }
 
 .block_button_back {
-    padding-top: 20px;
-}
+  padding-top: 20px;
+}
+
+.field_for_search {
+  width: 50%;
+  margin-left: 370px;
+}
+
+::-webkit-input-placeholder {
+  text-align: center;
+}
+
+.btn_block_search {
+  margin-top: 10px;
+  margin-left: 44%;
+  margin-bottom: 10px;
+}
+
+.btn_block_back {
+  padding: 10px;
+}
+
+.snippet_block {
+  display: flex;
+  flex-wrap: wrap;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+}
+
+.snippet {
+  margin-bottom: 10px;
+  border: 1px solid rgb(131, 111, 145);
+  border-radius: 10px;
+}
+
+.snippet img {
+  width: 100%;
+  padding: 10px;
+  height: 300px;
+  box-shadow: 0 6px 10px 0 rgba(152, 128, 168, 0.4);
+  transition: 0.3s;
+  border-radius: 10px;
+}
+
+.snippet img:hover {
+  box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
+}
+
+.block_content {
+  display: flex;
+  flex-wrap: wrap;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+}
+
+.block_content p {
+  font-size: 18px;
+  font-family: "Telefon Black", Sans-Serif;
+  line-height: 7px;
+}
+
+.btn_center {
+  margin-left: 200px;
+}

+ 164 - 0
src/pages/Project.js

@@ -0,0 +1,164 @@
+import { useEffect } from "react";
+import { connect } from "react-redux";
+import { actionSnippetById } from "../actions/actionSnippetById";
+import { useState } from "react";
+import { Editor } from "../components/Editor";
+import { actionSnippetAdd } from "../actions/actionSnippetAdd";
+import { Link } from "react-router-dom";
+
+const ProjectSnippet = ({
+  onSave,
+  getSnippet,
+  match: {
+    params: { id },
+  },
+  titleText,
+  descriptionText,
+  filesArr,
+  nameText,
+}) => {
+  useEffect(() => {
+    getSnippet(id);
+  }, []);
+  const [files, setFiles] = useState([]);
+  const [name, setName] = useState("");
+  const [title, setTitle] = useState("");
+  const [description, setDescription] = useState("");
+  useEffect(() => {
+    setFiles(filesArr);
+    setTitle(titleText);
+    setDescription(descriptionText);
+  }, [filesArr, titleText, descriptionText]);
+  const [srcDoc, setSrcDoc] = useState("");
+  const html = files?.filter((el) => {
+    return el?.type === "html";
+  })[0]?.text;
+  const css = files?.filter((el) => {
+    return el?.type === "css";
+  })[0]?.text;
+  const js = files?.filter((el) => {
+    return el?.type === "javascript";
+  })[0]?.text;
+  console.log(js);
+  useEffect(() => {
+    const timeout = setTimeout(() => {
+      setSrcDoc(`
+        <html>
+          <body>${html || ""}</body>
+          <style>${css}</style>
+          <script>${js}</script>
+        </html>
+      `);
+    }, 250);
+
+    return () => clearTimeout(timeout);
+  }, [html, css, js]);
+  return (
+    <div>
+      <br />
+      {files?.map((data, index) => (
+        <>
+          <Editor
+            onDelete={() => setFiles(files?.filter((item) => item !== data))}
+            data={data}
+            onChange={({ type, name, text }) =>
+              setFiles([
+                ...files.slice(0, index),
+                { type, name, text },
+                ...files.slice(index + 1),
+              ])
+            }
+          />
+        </>
+      ))}
+      <br />
+      <div
+        style={{
+          alignItems: "center",
+          textAlign: "center",
+          marginBottom: "10px",
+        }}
+      >
+        <div>
+          <button
+            className="btn btn-primary"
+            onClick={() => setFiles([...files, { type: "html" }])}
+            key={files}
+          >
+            Add editor
+          </button>
+          <iframe
+            srcDoc={srcDoc}
+            title="output"
+            sandbox="allow-scripts"
+            frameBorder="0"
+            width="95%"
+            height="95%"
+            style={{
+              marginTop: "10px",
+              border: "1px solid #F0FFFF",
+              boxShadow: "0px 5px 10px 2px rgba(34, 60, 80, 0.2)",
+            }}
+          />
+          <div>
+            <Link to="/projects">
+              <button className="btn btn-outline-info border-info mt-3">
+                All projects
+              </button>
+            </Link>
+          </div>
+        </div>
+        <div className="input-group mb-3 mt-5 ml-auto mr-auto w-25">
+          <div className="input-group-prepend">
+            <span className="input-group-text" id="basic-addon1">
+              Name of your project
+            </span>
+          </div>
+          <input
+            value={title}
+            onChange={(e) => setTitle(e.target.value)}
+            type="text"
+            className="form-control"
+            placeholder="Name of project"
+            aria-label="Name of project"
+            aria-describedby="basic-addon1"
+          />
+        </div>
+        <div className="input-group ml-auto mr-auto w-50">
+          <div className="input-group-prepend">
+            <span className="input-group-text ">Description</span>
+          </div>
+          <textarea
+            value={description}
+            onChange={(e) => setDescription(e.target.value)}
+            className="form-control "
+            aria-label="With textarea"
+            placeholder="Your description"
+          ></textarea>
+        </div>
+        <button
+          className="btn btn-success float-right mr-5 mb-5"
+          onClick={() => onSave(title, description, files, id)}
+        >
+          Save project
+        </button>
+      </div>
+    </div>
+  );
+};
+
+const ConnectedProject = connect(
+  (state) => ({
+    titleText:
+      state?.promise?.findSnippetById?.payload?.data?.SnippetFind?.[0]?.title,
+    descriptionText:
+      state?.promise?.findSnippetById?.payload?.data?.SnippetFind?.[0]
+        ?.description,
+    nameText:
+      state?.promise?.findSnippetById?.payload?.data?.SnippetFind?.[0]?.name,
+    filesArr:
+      state?.promise?.findSnippetById?.payload?.data?.SnippetFind?.[0]?.files,
+  }),
+  { getSnippet: actionSnippetById, onSave: actionSnippetAdd }
+)(ProjectSnippet);
+export default ConnectedProject;

+ 61 - 0
src/pages/Projects.js

@@ -0,0 +1,61 @@
+import { connect } from "react-redux";
+import { Link } from "react-router-dom";
+
+const Projects = ({ snippets }) => {
+  return snippets ? (
+    <div>
+      <Link to="/cabinet">
+        <button className="float-left btn-secondary d-inline-block mt-2 ml-2">
+          Back to Cabinet
+        </button>
+      </Link>
+      <br /> <br />
+      <div style={{ alignItems: "center", textAlign: "center" }}>
+        <Link to="/work">
+          <button className="btn btn-success">New project</button>
+        </Link>
+      </div>
+      {snippets?.map((key, index) => (
+        <div style={{ textAlign: "center", alignItems: "center" }}>
+          <div className="card w-50 ml-auto mr-auto mt-3 mb-5" style = {{boxShadow:'0px 5px 10px 2px rgba(34, 60, 80, 0.2)'}}>
+            <div className="card-body" style={{ textAlign: "center" }}>
+              <h3 className="card-title mb-4 text-info">
+                {snippets?.[index]?.title || "Project without name"}
+              </h3>
+              <p className="card-text">
+                <span className="text-muted">Description</span>&nbsp;
+                {snippets?.[index]?.description || ""}
+              </p>
+              <Link to={"/project/" + snippets?.[index]?._id}>
+                <button className="btn btn-primary mt-3">Open project</button>
+              </Link>
+            </div>
+          </div>
+        </div>
+      ))}
+    </div>
+  ) : (
+    <div>
+      <Link to="/">
+        <button className="float-left btn-secondary d-inline-block mt-2 ml-2">
+          Back to Main Page
+        </button>
+      </Link>{" "}
+      <br /> <br />
+      <div className="d-flex justify-content-center">
+        <div
+          className="spinner-border mt-3"
+          style={{ width: "10rem", height: "10rem" }}
+          role="status"
+        >
+          <span className="sr-only">Loading...</span>
+        </div>
+      </div>
+    </div>
+  );
+};
+
+const CProjects = connect((state) => ({
+  snippets: state?.p?.findSnippet?.payload?.data?.SnippetFind,
+}))(Projects);
+export default CProjects;

+ 55 - 47
src/pages/Search.js

@@ -2,56 +2,64 @@ import { actionSearch } from "../actions/actionSearch";
 import { Link } from "react-router-dom";
 import { connect } from "react-redux";
 import { useState } from "react";
+import code from "../../src/code.png";
 import "./Main.css";
 
 const Search = ({ onSearch, snippets }) => {
-    const [request, setRequest] = useState('');
-    return (
-        <>
-            <Link to="/work">
-                <button>
-                    Back to Main Page
-                </button>
-            </Link>
-            <br />
-            <br />
-            <div>
-                <input
-                    value={request}
-                    onChange={(e) => setRequest(e.target.value)}
-                    type="search"
-                    placeholder="Name of project"
-                    aria-label="Search"
-                />
-                <br />
-                <button onClick={() => onSearch(request)}>
-                    Search
-                </button>
+  const [request, setRequest] = useState("");
+  return (
+    <>
+      <Link to="/work">
+        <div className="btn_block_back">
+          <button className="btn_search">Back to Work Page</button>
+        </div>
+      </Link>
+      <br />
+      <br />
+      <div>
+        <input
+          className="field_for_search"
+          value={request}
+          onChange={(e) => setRequest(e.target.value)}
+          type="search"
+          placeholder="Type name or description"
+          aria-label="Search"
+        />
+        <br />
+        <div className="btn_block_search">
+          <button className="btn_search" onClick={() => onSearch(request)}>
+            Search
+          </button>
+        </div>
+      </div>
+      <div className="snippet_block">
+        {snippets?.map((key, index) => (
+          <div className="snippet">
+            <img src={code} alt="code"></img>
+            <div className="block_content">
+              <p>
+                {`Name: ${snippets?.[index]?.title}` || "Project without name"}
+              </p>
+              <p>{`Description: ${snippets?.[index]?.description}` || ""}</p>
+              <p>{`Owner: ${snippets?.[index]?.owner?.login}`}</p>
+              <div className="btn_center">
+                <Link to={"/project/" + snippets?.[index]?._id}>
+                  <button className="btn_search">Open project</button>
+                </Link>
+              </div>
             </div>
+          </div>
+        ))}
+      </div>
+    </>
+  );
+};
 
-            {snippets?.map((key, index) => (
-                <div>
-                    <div>
-                        <div>
-                            <h3>
-                                {snippets?.[index]?.title || "Project without name"}
-                            </h3>
-                            <p>
-                                <span>Description</span>&nbsp;
-                                {snippets?.[index]?.description || ""}
-                            </p>
-                            <p>{`Owner: ${snippets?.[index]?.owner?.login}`}</p>
-                            <Link to={"/project/" + snippets?.[index]?._id}>
-                                <button>Open project</button>
-                            </Link>
-                        </div>
-                    </div>
-                </div>
-            ))}
-        </>
-    )
-}
+const ConnectFormSearch = connect(
+  (state) => ({
+    snippets: state?.p?.searchSnippet?.payload?.data?.SnippetFind,
+  }),
+  { onSearch: actionSearch }
+)(Search);
 
-const ConnectFormSearch = connect(state => ({ snippets: state?.p?.searchSnippet?.payload?.data?.SnippetFind }), { onSearch: actionSearch })(Search)
-
-export default ConnectFormSearch;
+export default ConnectFormSearch;