Skip to content

Commit

Permalink
Code Change
Browse files Browse the repository at this point in the history
  • Loading branch information
zhuochenggan committed Nov 18, 2023
1 parent fcc4514 commit ff50240
Show file tree
Hide file tree
Showing 20 changed files with 722 additions and 39 deletions.
1 change: 0 additions & 1 deletion README.md

This file was deleted.

1 change: 1 addition & 0 deletions app/.deploy/settings.sample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

1 change: 1 addition & 0 deletions app/.meteor/packages
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ hot-module-replacement@0.5.3
dev-error-overlay@0.1.2
http
ostrio:files
webapp
2 changes: 2 additions & 0 deletions app/client/main.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import '../imports/startup/client/Startup';
import 'bootstrap/dist/css/bootstrap.min.css';
import './style.css';


2 changes: 1 addition & 1 deletion app/client/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@

.landing-white-background img {
margin-bottom: 50px;
}
}
22 changes: 22 additions & 0 deletions app/imports/api/modcard/modcard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';

class ModCardsCollection {
constructor() {
this.name = 'ModCardsCollection';
this.collection = new Mongo.Collection(this.name);
this.schema = new SimpleSchema({
type: String,
image: { type: String, optional: true },
cost: String,
detail: String,
address: String,
createdAt: Date,
owner: String,
});
this.collection.attachSchema(this.schema);
this.userPublicationName = `${this.name}.publication.user`;
}
}

export const ModCards = new ModCardsCollection();
8 changes: 1 addition & 7 deletions app/imports/startup/server/Method.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,14 @@ import { Meteor } from 'meteor/meteor';
import { HTTP } from 'meteor/http';
import cloudinary from 'cloudinary';

cloudinary.config({
cloud_name: 'you-own-key',
api_key: 'you-own-key',
api_secret: 'you-own-key',
});

Meteor.methods({
// eslint-disable-next-line meteor/audit-argument-checks
async generateImage(prompt) {
try {
const response = await HTTP.call('POST', 'https://api.openai.com/v1/images/generations', {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${'you-own-key'}`,
Authorization: `Bearer ${'your key'}`,
},
data: {
model: 'dall-e-3',
Expand Down
13 changes: 13 additions & 0 deletions app/imports/startup/server/Mongo.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Posts } from '../../api/post/post';
import { Comments } from '../../api/comment/comment';
import { Surveys } from '../../api/survey/survey';
import { Votes } from '../../api/vote/vote';
import { ModCards } from '../../api/modcard/modcard';

const addProfile = (profile) => {
console.log(` Adding: ${profile.firstName} (${profile.owner})`);
Expand Down Expand Up @@ -64,3 +65,15 @@ if (Comments.collection.find().count() === 0) {
Meteor.settings.defaultComments.forEach(data => addComment(data));
}
}

const addModCard = (modcard) => {
console.log(` Adding: ${modcard.address}`);
ModCards.collection.insert(modcard);
};

if (ModCards.collection.find().count() === 0) {
if (Meteor.settings.defaultModCards) {
console.log('Creating default modcards.');
Meteor.settings.defaultModCards.forEach(data => addModCard(data));
}
}
8 changes: 8 additions & 0 deletions app/imports/startup/server/Publications.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Posts } from '../../api/post/post';
import { Comments } from '../../api/comment/comment';
import { Surveys } from '../../api/survey/survey';
import { Votes } from '../../api/vote/vote';
import { ModCards } from '../../api/modcard/modcard';

Meteor.publish(Comments.userPublicationName, function () {
if (this.userId) {
Expand Down Expand Up @@ -32,6 +33,13 @@ Meteor.publish(Votes.userPublicationName, function () {
return this.ready();
});

Meteor.publish(ModCards.userPublicationName, function () {
if (this.userId) {
return ModCards.collection.find();
}
return this.ready();
});

Meteor.publish(Surveys.userPublicationName, function () {
if (this.userId) {
return Surveys.collection.find();
Expand Down
74 changes: 74 additions & 0 deletions app/imports/ui/components/ModCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React, { useState } from 'react';
import { Button, Card, Col, ListGroup, OverlayTrigger, Popover } from 'react-bootstrap';
import PropTypes from 'prop-types';

const ModCard = ({ modCard, onImageClick }) => {
const [show, setShow] = useState(false);
const detailLengthLimit = 55;

const renderPopover = (
<Popover id="popover-basic">
<Popover.Body>
{modCard.detail}
</Popover.Body>
</Popover>
);

const detailDisplay = modCard.detail.length > detailLengthLimit
? `${modCard.detail.substring(0, detailLengthLimit)}...`
: modCard.detail;

return (
<Col>
<Card style={{ width: '100%' }}>
<Card.Body>
<Card.Title style={{ fontSize: '1.1rem' }}>{modCard.type}</Card.Title>
</Card.Body>
<ListGroup className="list-group-flush">
<ListGroup.Item>Cost: {modCard.cost}</ListGroup.Item>
{modCard.image && (
<Button style={{ background: 'white', color: 'black' }} onClick={() => onImageClick(modCard.image)}>
Show Image
</Button>
)}
<ListGroup.Item>
Details: { /* space */}
{modCard.detail.length > detailLengthLimit ? (
<OverlayTrigger
trigger={['hover', 'focus']}
placement="right"
overlay={renderPopover}
show={show}
onToggle={(nextShow) => setShow(nextShow)}
>
<span style={{ cursor: 'pointer' }}>
{detailDisplay}
</span>
</OverlayTrigger>
) : (
detailDisplay
)}
<p className="p-2" style={{ fontSize: '0.6rem', color: '#6c757d' }}>
{modCard.owner} @ {modCard.createdAt.toLocaleDateString('en-US')}
</p>
</ListGroup.Item>
</ListGroup>
</Card>
</Col>
);
};

ModCard.propTypes = {
modCard: PropTypes.shape({
type: PropTypes.string.isRequired,
cost: PropTypes.string.isRequired,
image: PropTypes.string,
detail: PropTypes.string.isRequired,
createdAt: PropTypes.instanceOf(Date).isRequired,
owner: PropTypes.string.isRequired,
_id: PropTypes.string.isRequired,
}).isRequired,
onImageClick: PropTypes.func.isRequired,
};

export default ModCard;
6 changes: 3 additions & 3 deletions app/imports/ui/components/NavBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ const NavBar = () => {
) : ''}
{currentUser ? (
<NavDropdown title="Visualize Toolset" className="white-text-dropdown">
<NavDropdown.Item as={NavLink} to="/model">
<Nav.Link as={NavLink} to="/model" key="model" style={{ color: 'black' }}>Model</Nav.Link>
</NavDropdown.Item>
<NavDropdown.Item as={NavLink} to="/dalle3">
<Nav.Link as={NavLink} to="/dalle3" key="dalle" style={{ color: 'black' }}>Dall-E3</Nav.Link>
</NavDropdown.Item>
<NavDropdown.Item as={NavLink} to="/mapping">
<Nav.Link as={NavLink} to="/mapping" key="gis" style={{ color: 'black' }}>Gis-Map</Nav.Link>
</NavDropdown.Item>
<NavDropdown.Item as={NavLink} to="/data">
<Nav.Link as={NavLink} to="/data" key="data" style={{ color: 'black' }}>Data (Coming Soon) </Nav.Link>
</NavDropdown.Item>
<NavDropdown.Item as={NavLink} to="/external">
<Nav.Link as={NavLink} to="/external" key="external" style={{ color: 'black' }}>External</Nav.Link>
</NavDropdown.Item>
Expand Down
6 changes: 4 additions & 2 deletions app/imports/ui/layouts/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import AddPost from '../pages/AddPost';
import Survey from '../pages/SurveyPage';
import AddSurvey from '../pages/AddSurvey';
import Mapping from '../pages/Mapping';
import Data from '../pages/Data';
import Model from '../pages/Model';
import AddModCard from '../pages/AddModCard';

const App = () => {
const { ready } = useTracker(() => {
Expand Down Expand Up @@ -51,7 +52,8 @@ const App = () => {
<Route path="/addprofile" element={<ProtectedRoute><AddProfile /></ProtectedRoute>} />
<Route path="/addpost" element={<ProtectedRoute><AddPost /></ProtectedRoute>} />
<Route path="/addsurvey" element={<ProtectedRoute><AddSurvey /></ProtectedRoute>} />
<Route path="/data" element={<ProtectedRoute><Data /></ProtectedRoute>} />
<Route path="/addmodcard" element={<ProtectedRoute><AddModCard /></ProtectedRoute>} />
<Route path="/model" element={<ProtectedRoute><Model /></ProtectedRoute>} />
<Route path="/admin" element={<AdminProtectedRoute ready={ready}><Landing /></AdminProtectedRoute>} />
<Route path="/notauthorized" element={<NotAuthorized />} />
<Route path="*" element={<NotFound />} />
Expand Down
89 changes: 89 additions & 0 deletions app/imports/ui/pages/AddModCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React, { useState } from 'react';
import { useLocation } from 'react-router-dom';
import { Meteor } from 'meteor/meteor';
import { Container, Row, Col, Card } from 'react-bootstrap';
import { AutoForm, TextField, LongTextField, SubmitField, ErrorsField, HiddenField } from 'uniforms-bootstrap5';
import swal from 'sweetalert';
import SimpleSchema2Bridge from 'uniforms-bridge-simple-schema-2';
import { ModCards } from '../../api/modcard/modcard';
import FileField from '../components/FileField';

const bridge = new SimpleSchema2Bridge(ModCards.schema);

const AddModCard = () => {
const [imageFile, setImageFile] = useState(null);
let fRef = null;
const user = Meteor.user();

const location = useLocation();
const currentAddress = location.state?.formattedAddress || '';

const handleImageChange = (file) => {
setImageFile(file);
};

const submit = (data) => {
const { image, ...modCardData } = data;
modCardData.address = currentAddress;

// eslint-disable-next-line no-shadow
const insertModCard = (modCardData) => {
ModCards.collection.insert(modCardData, (error) => {
if (error) {
swal('Error', error.message, 'error');
} else {
swal('Success', 'ModCard added successfully', 'success');
fRef.reset();
}
});
};

if (imageFile) {
const reader = new FileReader();
reader.onloadend = function () {
const fileData = reader.result;

Meteor.call('uploadImage', fileData, (error, imageUrl) => {
if (error) {
swal('Error', 'Failed to upload image.', 'error');
} else {
modCardData.image = imageUrl;
insertModCard(modCardData);
}
});
};
reader.readAsDataURL(imageFile);
} else {
insertModCard(modCardData);
}
};

return (
<Container className="py-3">
<Row className="justify-content-center">
<Col xs={6}>
<Col className="text-center"><h2>Add ModCard</h2></Col>
<AutoForm ref={ref => { fRef = ref; }} schema={bridge} onSubmit={submit}>
<Card>
<Card.Body>
<TextField name="type" />
<div className="mb-3">
<FileField name="image" onChange={handleImageChange} />
</div>
<TextField name="cost" />
<LongTextField name="detail" />
<ErrorsField />
<SubmitField value="Submit" />
<HiddenField name="createdAt" value={new Date()} />
<HiddenField name="address" value={currentAddress} />
{user ? <HiddenField name="owner" value={user.username} /> : null}
</Card.Body>
</Card>
</AutoForm>
</Col>
</Row>
</Container>
);
};

export default AddModCard;
15 changes: 0 additions & 15 deletions app/imports/ui/pages/Data.jsx

This file was deleted.

19 changes: 11 additions & 8 deletions app/imports/ui/pages/Landing.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { Col, Container, Image, Row } from 'react-bootstrap';
import { EmojiSmile } from 'react-bootstrap-icons';

const Landing = () => (
<div>
Expand All @@ -10,7 +11,8 @@ const Landing = () => (
<Col xs={6}>
<Image
src="/images/Generative-AI.png"
width={500}
width="80%"
height="80%"
className="image-spacing"
style={{
boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.5)' }}
Expand All @@ -19,8 +21,8 @@ const Landing = () => (
<Col xs={6}>
<Image
src="/images/Embedded-VR.png"
width={500}
height={315}
width="80%"
height="80%"
className="image-spacing"
style={{
boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.5)' }}
Expand All @@ -37,9 +39,9 @@ const Landing = () => (
<Col xs={6}>
<br />
<h2 style={{ color: 'white' }}>Our Mission</h2>
<br />
{/* eslint-disable-next-line max-len */}
<p style={{ color: 'white', fontSize: '20px' }}>We want to create a web portal that facilitates the civic engagement around how to rebuild Lahaina, Maui, including surveys, forums, sharing design concepts with visual and data models for economic, environmental, etc. analysis.</p>
<p style={{ color: 'white', fontSize: '110%' }}>We want to create a web portal that facilitates the civic engagement around how to rebuild Lahaina, Maui, including surveys, forums, sharing design concepts with visual and mappinig models for economic, environmental, etc. analysis.</p>
<EmojiSmile style={{ color: 'white' }} size="25%" />
</Col>
</Row>
</Container>
Expand All @@ -53,7 +55,8 @@ const Landing = () => (
<Col xs={6}>
<Image
src="/images/forum-page2.png"
width={500}
width="80%"
height="80%"
className="image-spacing"
style={{
boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.5)' }}
Expand All @@ -62,8 +65,8 @@ const Landing = () => (
<Col xs={6}>
<Image
src="/images/Survey-Page.png"
width={500}
height={325}
width="80%"
height="80%"
className="image-spacing"
style={{
boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.5)' }}
Expand Down
Loading

0 comments on commit ff50240

Please sign in to comment.