├── .meteor
├── cordova-plugins
├── release
├── platforms
├── .gitignore
├── .id
├── packages
├── .finished-upgraders
└── versions
├── .eslintignore
├── packages
├── zensroom
│ ├── tests
│ │ ├── index.js
│ │ └── models
│ │ │ ├── bookings
│ │ │ ├── schema.spec.js
│ │ │ ├── collection.spec.js
│ │ │ ├── fragments.spec.js
│ │ │ ├── mutations.spec.js
│ │ │ ├── parameters.spec.js
│ │ │ ├── resolvers.spec.js
│ │ │ └── permissions.spec.js
│ │ │ ├── reviews
│ │ │ ├── schema.spec.js
│ │ │ ├── fragments.spec.js
│ │ │ ├── mutations.spec.js
│ │ │ ├── resolvers.spec.js
│ │ │ ├── collection.spec.js
│ │ │ ├── parameters.spec.js
│ │ │ └── permissions.spec.js
│ │ │ └── properties
│ │ │ ├── schema.spec.js
│ │ │ ├── collection.spec.js
│ │ │ ├── fragments.spec.js
│ │ │ ├── mutations.spec.js
│ │ │ ├── resolvers.spec.js
│ │ │ ├── parameters.spec.js
│ │ │ └── permissions.spec.js
│ ├── lib
│ │ ├── stylesheets
│ │ │ ├── _breakpoints.scss
│ │ │ ├── _typography.scss
│ │ │ ├── _home.scss
│ │ │ ├── _footer.scss
│ │ │ ├── _reviews.scss
│ │ │ ├── _forms.scss
│ │ │ ├── _bookings.scss
│ │ │ ├── main.scss
│ │ │ ├── _colors.scss
│ │ │ ├── _global.scss
│ │ │ ├── _variables.scss
│ │ │ ├── _users.scss
│ │ │ ├── _admin.scss
│ │ │ ├── _spinner.scss
│ │ │ ├── _header.scss
│ │ │ ├── _accounts.scss
│ │ │ └── _rooms.scss
│ │ ├── client
│ │ │ └── main.js
│ │ ├── server
│ │ │ ├── emails
│ │ │ │ ├── common
│ │ │ │ │ ├── test.handlebars
│ │ │ │ │ └── wrapper.handlebars
│ │ │ │ ├── rooms
│ │ │ │ │ └── roomsNew.handlebars
│ │ │ │ ├── bookings
│ │ │ │ │ └── bookingsNew.handlebars
│ │ │ │ └── templates.js
│ │ │ ├── indexes.js
│ │ │ ├── main.js
│ │ │ ├── seed.js
│ │ │ ├── bookings
│ │ │ │ └── callbacks.js
│ │ │ └── rooms
│ │ │ │ └── callbacks.js
│ │ ├── modules
│ │ │ ├── icons.js
│ │ │ ├── reviews
│ │ │ │ ├── views.js
│ │ │ │ ├── permissions.js
│ │ │ │ ├── collection.js
│ │ │ │ ├── schema.js
│ │ │ │ └── check.js
│ │ │ ├── rooms
│ │ │ │ ├── views.js
│ │ │ │ ├── permissions.js
│ │ │ │ ├── fragments.js
│ │ │ │ ├── collection.js
│ │ │ │ ├── parameters.js
│ │ │ │ └── schema.js
│ │ │ ├── bookings
│ │ │ │ ├── permissions.js
│ │ │ │ ├── collection.js
│ │ │ │ ├── fragments.js
│ │ │ │ ├── resolvers.js
│ │ │ │ ├── views.js
│ │ │ │ └── schema.js
│ │ │ ├── index.js
│ │ │ ├── fragments.js
│ │ │ ├── products.js
│ │ │ ├── admin.js
│ │ │ ├── data.js
│ │ │ ├── emails.js
│ │ │ ├── i18n.js
│ │ │ ├── components.js
│ │ │ ├── custom_fields.js
│ │ │ └── routes.js
│ │ ├── components
│ │ │ ├── static
│ │ │ │ ├── About.js
│ │ │ │ ├── HowTo.js
│ │ │ │ ├── Terms.js
│ │ │ │ └── Privacy.js
│ │ │ ├── common
│ │ │ │ ├── Footer.js
│ │ │ │ ├── FlashMessages.js
│ │ │ │ ├── Home.js
│ │ │ │ ├── Flash.js
│ │ │ │ ├── Layout.js
│ │ │ │ ├── AdminLayout.js
│ │ │ │ └── Header.js
│ │ │ ├── reviews
│ │ │ │ ├── ReviewsItem.js
│ │ │ │ ├── ReviewsList.js
│ │ │ │ └── ReviewsNewForm.js
│ │ │ ├── rooms
│ │ │ │ ├── RoomsNewPage.js
│ │ │ │ ├── RoomsReviews.js
│ │ │ │ ├── RoomsItem.js
│ │ │ │ ├── RoomsList.js
│ │ │ │ ├── RoomsNewForm.js
│ │ │ │ ├── RoomsMain.js
│ │ │ │ ├── RoomsPhotos.js
│ │ │ │ ├── RoomsPage.js
│ │ │ │ ├── RoomsSearchResults.js
│ │ │ │ ├── RoomsSearch.js
│ │ │ │ ├── RoomsSearchFilters.js
│ │ │ │ └── RoomsSearchForm.js
│ │ │ ├── admin
│ │ │ │ ├── AdminUsersRooms.js
│ │ │ │ ├── AdminUsersReviews.js
│ │ │ │ ├── AdminUsersBookings.js
│ │ │ │ ├── ReviewsDashboard.js
│ │ │ │ ├── RoomsDashboard.js
│ │ │ │ └── BookingsDashboard.js
│ │ │ ├── users
│ │ │ │ ├── UsersSignUp.jsx
│ │ │ │ ├── UsersLogIn.jsx
│ │ │ │ ├── UsersAccountMenu.js
│ │ │ │ ├── UsersProfile.js
│ │ │ │ ├── UsersAccount.js
│ │ │ │ └── UsersMenu.js
│ │ │ └── bookings
│ │ │ │ ├── BookingsPending.js
│ │ │ │ ├── BookingsFuture.js
│ │ │ │ ├── BookingsCurrent.js
│ │ │ │ ├── BookingsPast.js
│ │ │ │ ├── BookingsRoomUser.js
│ │ │ │ ├── BookingsCompleted.js
│ │ │ │ ├── BookingsPage.js
│ │ │ │ └── BookingsNewForm.js
│ │ └── containers
│ │ │ └── withUnavailableDatesContainer.js
│ └── package.js
├── vulcan-maps
│ ├── lib
│ │ ├── modules
│ │ │ ├── headtags.js
│ │ │ ├── index.js
│ │ │ └── components.js
│ │ ├── client
│ │ │ └── main.js
│ │ ├── server
│ │ │ ├── main.js
│ │ │ └── maps.js
│ │ └── components
│ │ │ └── Map.js
│ └── package.js
├── .gitignore
├── _buffer
│ ├── buffer.js
│ └── package.js
└── _boilerplate-generator
│ ├── package.js
│ ├── template-web.browser.js
│ ├── template-web.cordova.js
│ └── generator.js
├── Dockerfile
├── publish_packages.sh
├── .github
└── CONTRIBUTING.md
├── jsdoc-conf.json
├── .editorconfig
├── .yo-rc.json
├── CONTRIBUTING.md
├── .gitignore
├── license.md
├── jsdoc.json
├── prestart_vulcan.sh
├── .eslintrc
├── sample_settings.json
├── package.json
├── README.md
├── .jshintrc
└── History.md
/.meteor/cordova-plugins:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | packages/_*
2 |
--------------------------------------------------------------------------------
/packages/zensroom/tests/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.meteor/release:
--------------------------------------------------------------------------------
1 | METEOR@1.5.2.1
2 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM abernix/meteord:onbuild
--------------------------------------------------------------------------------
/.meteor/platforms:
--------------------------------------------------------------------------------
1 | server
2 | browser
3 |
--------------------------------------------------------------------------------
/packages/vulcan-maps/lib/modules/headtags.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_breakpoints.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_typography.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.meteor/.gitignore:
--------------------------------------------------------------------------------
1 | dev_bundle
2 | local
3 | meteorite
4 |
--------------------------------------------------------------------------------
/packages/.gitignore:
--------------------------------------------------------------------------------
1 | /bootstrap3-datepicker
2 | /npm-container
--------------------------------------------------------------------------------
/packages/vulcan-maps/lib/modules/index.js:
--------------------------------------------------------------------------------
1 | import './components';
--------------------------------------------------------------------------------
/packages/vulcan-maps/lib/client/main.js:
--------------------------------------------------------------------------------
1 | import '../modules/index';
2 |
--------------------------------------------------------------------------------
/packages/vulcan-maps/lib/modules/components.js:
--------------------------------------------------------------------------------
1 | import '../components/Map';
--------------------------------------------------------------------------------
/packages/_buffer/buffer.js:
--------------------------------------------------------------------------------
1 | global.Buffer = global.Buffer || require("buffer").Buffer;
--------------------------------------------------------------------------------
/packages/vulcan-maps/lib/server/main.js:
--------------------------------------------------------------------------------
1 | import '../modules/index';
2 | export * from './maps';
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_home.scss:
--------------------------------------------------------------------------------
1 | .home-section{
2 | margin-bottom: $spacing * 2;
3 | }
--------------------------------------------------------------------------------
/publish_packages.sh:
--------------------------------------------------------------------------------
1 | for d in packages/* ; do
2 | echo "$d"
3 | cd $d
4 | meteor publish
5 | cd ../../
6 | done
--------------------------------------------------------------------------------
/packages/zensroom/lib/client/main.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | The client entry point for the package.
4 |
5 | */
6 |
7 | import '../modules/index.js';
--------------------------------------------------------------------------------
/packages/zensroom/lib/server/emails/common/test.handlebars:
--------------------------------------------------------------------------------
1 | This is just a test
2 |
3 | Sent at {{date}}.
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_footer.scss:
--------------------------------------------------------------------------------
1 | .footer{
2 | padding: $spacing;
3 | border-top: 1px solid #eee;
4 | margin-top: $spacing;
5 | text-align: center;
6 | }
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_reviews.scss:
--------------------------------------------------------------------------------
1 | .reviews-item{
2 | padding-bottom: $spacing;
3 | border-bottom: 1px solid $light-grey;
4 | margin-bottom: $spacing;
5 | }
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_forms.scss:
--------------------------------------------------------------------------------
1 | .input-amenities, .input-spaces{
2 | .col-sm-9{
3 | display: flex;
4 | flex-wrap: wrap;
5 | }
6 | .checkbox{
7 | width: 50%;
8 | }
9 | }
--------------------------------------------------------------------------------
/packages/zensroom/lib/server/indexes.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | MongoDB indexes for geographic search
4 |
5 | */
6 |
7 | import Rooms from '../modules/rooms/collection';
8 |
9 | Rooms.rawCollection().createIndex({location: '2dsphere'});
10 |
--------------------------------------------------------------------------------
/packages/_buffer/package.js:
--------------------------------------------------------------------------------
1 | Package.describe({
2 | name: "buffer"
3 | });
4 |
5 | Package.onUse( function(api) {
6 |
7 | api.use([
8 | 'ecmascript'
9 | ]);
10 |
11 | api.addFiles([
12 | 'buffer.js'
13 | ], ['client']);
14 |
15 | });
16 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/server/emails/rooms/roomsNew.handlebars:
--------------------------------------------------------------------------------
1 |
2 | {{RoomsSingle.user.displayName}}
3 | has added a new room:
4 | {{RoomsSingle.name}}
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/bookings/schema.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import schema from '../../../lib/models/bookings/schema';
3 |
4 | describe('The schema for: bookings', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/reviews/schema.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import schema from '../../../lib/models/reviews/schema';
3 |
4 | describe('The schema for: reviews', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/properties/schema.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import schema from '../../../lib/models/properties/schema';
3 |
4 | describe('The schema for: properties', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/reviews/fragments.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import fragments from '../../../lib/models/reviews/fragments';
3 |
4 | describe('The fragments for: reviews', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/reviews/mutations.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import mutations from '../../../lib/models/reviews/mutations';
3 |
4 | describe('The mutations for: reviews', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/reviews/resolvers.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import resolvers from '../../../lib/models/reviews/resolvers';
3 |
4 | describe('The resolvers for: reviews', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Before starting on a new feature, please [check out the roadmap](https://trello.com/b/dwPR0LTz/vulcanjs-roadmap) and come check-in in the [Vulcan Slack channel](http://slack.telescopeapp.org/).
2 |
3 | Also, all PRs should be made to the `devel` branch, not `master`.
4 |
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/bookings/collection.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import collection from '../../../lib/models/bookings/fragments';
3 |
4 | describe('The collection for: bookings', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/bookings/fragments.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import fragments from '../../../lib/models/bookings/fragments';
3 |
4 | describe('The fragments for: bookings', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/bookings/mutations.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import mutations from '../../../lib/models/bookings/mutations';
3 |
4 | describe('The mutations for: bookings', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/bookings/parameters.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import parameters from '../../../lib/models/bookings/parameters';
3 |
4 | describe('The parameters for: bookings', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/bookings/resolvers.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import resolvers from '../../../lib/models/bookings/resolvers';
3 |
4 | describe('The resolvers for: bookings', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/reviews/collection.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import collection from '../../../lib/models/reviews/fragments';
3 |
4 | describe('The collection for: reviews', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/reviews/parameters.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import parameters from '../../../lib/models/reviews/parameters';
3 |
4 | describe('The parameters for: reviews', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/jsdoc-conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["plugins/markdown"],
3 | "markdown": {
4 | "parser": "gfm"
5 | },
6 | "source": {
7 | "exclude": [
8 | ".git",
9 | ".meteor",
10 | "node_modules"
11 | ],
12 | "includePattern": ".+\\.js(x|doc)?$"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/server/main.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | The server entry point for the package.
4 |
5 | */
6 |
7 | import '../modules/index.js';
8 | import './indexes.js';
9 | import './emails/templates.js';
10 |
11 | import './bookings/callbacks.js';
12 |
13 | import './rooms/callbacks.js';
14 |
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/bookings/permissions.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import permissions from '../../../lib/models/bookings/permissions';
3 |
4 | describe('The permissions for: bookings', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/properties/collection.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import collection from '../../../lib/models/properties/fragments';
3 |
4 | describe('The collection for: properties', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/properties/fragments.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import fragments from '../../../lib/models/properties/fragments';
3 |
4 | describe('The fragments for: properties', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/properties/mutations.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import mutations from '../../../lib/models/properties/mutations';
3 |
4 | describe('The mutations for: properties', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/properties/resolvers.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import resolvers from '../../../lib/models/properties/resolvers';
3 |
4 | describe('The resolvers for: properties', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/reviews/permissions.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import permissions from '../../../lib/models/reviews/permissions';
3 |
4 | describe('The permissions for: reviews', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/properties/parameters.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import parameters from '../../../lib/models/properties/parameters';
3 |
4 | describe('The parameters for: properties', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/tests/models/properties/permissions.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 | import permissions from '../../../lib/models/properties/permissions';
3 |
4 | describe('The permissions for: properties', () => {
5 | it('should have some tests', () => {
6 | assert(false);
7 | });
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/lib/server/seed.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Seed database (not currently used)
4 |
5 | */
6 |
7 | // import Users from 'meteor/vulcan:users';
8 | // import { newMutation } from 'meteor/vulcan:core';
9 |
10 | // const seedData = [];
11 |
12 | // Meteor.startup(function() {
13 |
14 | // });
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_bookings.scss:
--------------------------------------------------------------------------------
1 | .rdtPicker td.rdtDisabled{
2 | color: red;
3 | }
4 |
5 | .bookings-form-field{
6 | margin-bottom: $spacing/2;
7 | }
8 |
9 | .bookings-form-submit{
10 | margin-top: $spacing;
11 | }
12 |
13 | .bookings-checkout{
14 | margin-bottom: $spacing;
15 | }
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/icons.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Icons
4 |
5 | http://docs.vulcanjs.org/core-components.html#Icon
6 |
7 | */
8 |
9 | import { Utils } from 'meteor/vulcan:core';
10 |
11 | Utils.icons.marker = 'map-marker';
12 | Utils.icons.previous = 'angle-left';
13 | Utils.icons.next = 'angle-right';
14 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/reviews/views.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Reviews views
4 |
5 | http://docs.vulcanjs.org/terms-parameters.html#Using-Views
6 |
7 | */
8 |
9 | import Reviews from './collection.js';
10 |
11 | Reviews.addView('roomReviews', terms => ({
12 | selector: {
13 | roomId: terms.roomId
14 | }
15 | }));
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/rooms/views.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Rooms views
4 |
5 | http://docs.vulcanjs.org/terms-parameters.html#Using-Views
6 |
7 | */
8 |
9 | import Rooms from './collection.js';
10 |
11 | Rooms.addView('roomsSearch', terms => ({
12 | options: {
13 | sort: {sticky: -1, baseScore: -1}
14 | }
15 | }));
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/rooms/permissions.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Rooms permissions
4 |
5 | http://docs.vulcanjs.org/groups-permissions.html#Assigning-Actions
6 |
7 | */
8 |
9 | import Users from 'meteor/vulcan:users';
10 |
11 | Users.groups.members.can([
12 | 'rooms.new',
13 | 'rooms.edit.own',
14 | 'rooms.remove.own',
15 | ]);
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/reviews/permissions.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Reviews permissions
4 |
5 | http://docs.vulcanjs.org/groups-permissions.html#Assigning-Actions
6 |
7 | */
8 |
9 | import Users from 'meteor/vulcan:users';
10 |
11 | Users.groups.members.can([
12 | 'reviews.new',
13 | 'reviews.edit.own',
14 | 'reviews.remove.own',
15 | ]);
--------------------------------------------------------------------------------
/.meteor/.id:
--------------------------------------------------------------------------------
1 | # This file contains a token that is unique to your project.
2 | # Check it into your repository along with the rest of this directory.
3 | # It can be used for purposes such as:
4 | # - ensuring you don't accidentally deploy one app on top of another
5 | # - providing package authors with aggregated statistics
6 |
7 | 4ibiemui4bqn1j744h7
8 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/bookings/permissions.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Bookings permissions
4 |
5 | http://docs.vulcanjs.org/groups-permissions.html#Assigning-Actions
6 |
7 | */
8 |
9 | import Users from 'meteor/vulcan:users';
10 |
11 | Users.groups.members.can([
12 | 'bookings.new',
13 | 'bookings.edit.own',
14 | 'bookings.remove.own',
15 | ]);
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/static/About.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | About page.
4 |
5 | */
6 |
7 | import React from 'react';
8 | import { Components, registerComponent } from 'meteor/vulcan:core';
9 |
10 | const About = () =>
11 |
12 |
13 |
14 | About
15 |
16 |
17 |
18 | registerComponent('About', About);
19 |
20 | export default About;
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/static/HowTo.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | How To page.
4 |
5 | */
6 |
7 | import React from 'react';
8 | import { Components, registerComponent } from 'meteor/vulcan:core';
9 |
10 | const HowTo = () =>
11 |
12 |
13 |
14 | HowTo
15 |
16 |
17 |
18 | registerComponent('HowTo', HowTo);
19 |
20 | export default HowTo;
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/static/Terms.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Terms page.
4 |
5 | */
6 |
7 | import React from 'react';
8 | import { Components, registerComponent } from 'meteor/vulcan:core';
9 |
10 | const Terms = () =>
11 |
12 |
13 |
14 | Terms
15 |
16 |
17 |
18 | registerComponent('Terms', Terms);
19 |
20 | export default Terms;
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/static/Privacy.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Privacy page.
4 |
5 | */
6 |
7 | import React from 'react';
8 | import { Components, registerComponent } from 'meteor/vulcan:core';
9 |
10 | const Privacy = () =>
11 |
12 |
13 |
14 | Privacy
15 |
16 |
17 |
18 | registerComponent('Privacy', Privacy);
19 |
20 | export default Privacy;
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/common/Footer.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Footer
4 |
5 | */
6 |
7 | import { registerComponent } from 'meteor/vulcan:core';
8 | import React from 'react';
9 |
10 | const Footer = props =>
11 |
14 |
15 |
16 | Footer.displayName = 'Footer';
17 |
18 | registerComponent('Footer', Footer);
19 |
20 | // export default Footer;
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 |
3 | root = true
4 |
5 | [*.{js,html}]
6 |
7 | charset = utf-8
8 | end_of_line = lf
9 | indent_brace_style = 1TBS
10 | indent_size = 2
11 | indent_style = space
12 | insert_final_newline = true
13 | max_line_length = 80
14 | quote_type = auto
15 | spaces_around_operators = true
16 | trim_trailing_whitespace = true
17 |
18 | [*.md]
19 |
20 | trim_trailing_whitespace = false
21 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/rooms/fragments.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Rooms fragments
4 |
5 | http://docs.vulcanjs.org/fragments.html
6 |
7 | */
8 |
9 | import { registerFragment } from 'meteor/vulcan:core';
10 |
11 | registerFragment(`
12 | fragment RoomsItemFragment on Room {
13 | _id
14 | createdAt
15 | name
16 | bedsNumber
17 | guestsNumber
18 | amenities
19 | photos
20 | }
21 | `);
22 |
--------------------------------------------------------------------------------
/packages/_boilerplate-generator/package.js:
--------------------------------------------------------------------------------
1 | Package.describe({
2 | name: "boilerplate-generator",
3 | summary: "Generates the boilerplate html from program's manifest",
4 | version: '1.2.0'
5 | });
6 |
7 | Package.onUse(api => {
8 | api.use('ecmascript');
9 | api.use([
10 | 'underscore',
11 | ], 'server');
12 | api.mainModule('generator.js', 'server');
13 | api.export('Boilerplate', 'server');
14 | });
15 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/reviews/ReviewsItem.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Review list item
4 |
5 | */
6 |
7 | import React from 'react';
8 | import { registerComponent } from 'meteor/vulcan:core';
9 |
10 | const ReviewsItem = ({ review }) =>
11 |
12 |
13 |
14 | {review.comment}
15 |
16 |
17 |
18 | registerComponent('ReviewsItem', ReviewsItem);
19 |
20 | // export default ReviewsItem;
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/main.scss:
--------------------------------------------------------------------------------
1 | // @import "bootstrap";
2 |
3 | @import "breakpoints";
4 | @import "typography";
5 | @import "colors";
6 | @import "variables";
7 | @import "spinner";
8 | @import "global";
9 |
10 | @import "accounts";
11 | @import "admin";
12 | @import "header";
13 | @import "users";
14 | @import "rooms";
15 | @import "bookings";
16 | @import "reviews";
17 | @import "forms";
18 | @import "home";
19 | @import "footer";
20 |
--------------------------------------------------------------------------------
/packages/vulcan-maps/package.js:
--------------------------------------------------------------------------------
1 | Package.describe({
2 | name: "vulcan:maps",
3 | summary: "Vulcan Google Maps package",
4 | version: '1.6.0',
5 | git: "https://github.com/VulcanJS/Vulcan.git"
6 | });
7 |
8 | Package.onUse(function (api) {
9 |
10 | api.versionsFrom("METEOR@1.0");
11 |
12 | api.use([
13 | 'vulcan:core@1.6.0',
14 | ]);
15 |
16 | api.mainModule('lib/server/main.js', 'server');
17 | api.mainModule('lib/client/main.js', 'client');
18 |
19 | });
20 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/server/emails/bookings/bookingsNew.handlebars:
--------------------------------------------------------------------------------
1 |
2 | {{BookingsSingle.user.displayName}}
3 | has booked room
4 | {{BookingsSingle.room.name}}
5 | :
6 |
7 |
8 |
9 | From {{BookingsSingle.startAtFormatted}} to {{BookingsSingle.endAtFormatted}}
10 |
11 |
12 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/server/emails/templates.js:
--------------------------------------------------------------------------------
1 | import VulcanEmail from 'meteor/vulcan:email';
2 |
3 | VulcanEmail.addTemplates({
4 | test: Assets.getText("lib/server/emails/common/test.handlebars"),
5 | wrapper: Assets.getText("lib/server/emails/common/wrapper.handlebars"),
6 | roomsNew: Assets.getText("lib/server/emails/rooms/roomsNew.handlebars"),
7 | bookingsNew: Assets.getText("lib/server/emails/bookings/bookingsNew.handlebars"),
8 | });
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/index.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Modules entry point
4 |
5 | */
6 |
7 | import './fragments';
8 | import './icons';
9 | import './custom_fields';
10 | import './i18n';
11 | import './components';
12 | import './admin';
13 | import './routes';
14 | import './products';
15 | import './emails';
16 |
17 | import './rooms/collection';
18 |
19 | import './reviews/collection';
20 | import './reviews/views';
21 |
22 | import './bookings/collection';
23 | import './bookings/views';
24 |
25 |
26 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/server/bookings/callbacks.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Server callbacks
4 |
5 | See: http://docs.vulcanjs.org/callbacks.html
6 |
7 | */
8 |
9 | import { addCallback } from 'meteor/vulcan:core';
10 |
11 | /*
12 |
13 | When a booking is paid, mark it as paid
14 |
15 | */
16 | function setBookingPaidAt (modifier, post, charge) {
17 | modifier.$set.paidAt = new Date();
18 | modifier.$set.status = 3; // mark as paid
19 | return modifier;
20 | }
21 | addCallback('bookings.charge.sync', setBookingPaidAt);
22 |
--------------------------------------------------------------------------------
/.meteor/packages:
--------------------------------------------------------------------------------
1 | # see http://docs.vulcanjs.org/packages
2 |
3 | vulcan:core
4 |
5 | ############ Language Packages ############
6 |
7 | vulcan:i18n-en-us
8 |
9 | ############ Accounts Packages ############
10 |
11 | accounts-password@1.4.0
12 | # accounts-twitter
13 | # accounts-facebook
14 |
15 | ############ Your Packages ############
16 |
17 | # example-movies
18 | # example-instagram
19 | # example-forum
20 | # example-customization
21 | # example-permissions
22 | # example-membership
23 | # example-interfaces
24 |
25 | vulcan:debug
26 | zensroom
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_colors.scss:
--------------------------------------------------------------------------------
1 | $light-yellow: #FFFBDB;
2 |
3 | $lightest-grey: #f3f3f3;
4 | $lighter-grey: #eee;
5 | $light-grey: #ddd;
6 |
7 | $light-blue: #DAEDFF;
8 | $blue: #0275d8;
9 | $white: #fff;
10 | $medium-grey: #bbb;
11 | $dark-grey: #888;
12 | $black: #333;
13 |
14 | $red: #E04E4B;
15 |
16 | $header-bg: $light-blue;
17 |
18 | $active-color: $blue;
19 |
20 | $lightest-border: $lightest-grey;
21 | $lighter-border: $lighter-grey;
22 | $light-border: $light-grey;
23 |
24 | $light-text: $medium-grey;
25 | $medium-text: $dark-grey;
26 |
--------------------------------------------------------------------------------
/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "vulcanjs-cli": {
3 | "appName": "zensroom",
4 | "isVulcan": true,
5 | "packageManager": "yarn",
6 | "reactExtension": "jsx",
7 | "storyBook": {
8 | "isUsed": "pending",
9 | "setupStatus": "pending"
10 | },
11 | "packages": {
12 | "zensroom": {
13 | "models": {
14 | "properties": {},
15 | "reviews": {},
16 | "bookings": {}
17 | },
18 | "routes": {
19 | "home": {
20 | "routePath": "/"
21 | }
22 | }
23 | }
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/rooms/collection.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Rooms collection
4 |
5 | http://docs.vulcanjs.org/schemas.html
6 |
7 | */
8 |
9 | import { createCollection, getDefaultResolvers, getDefaultMutations } from 'meteor/vulcan:core';
10 | import schema from './schema.js';
11 | import './fragments.js';
12 | import './permissions.js';
13 | import './parameters.js';
14 |
15 | const Rooms = createCollection({
16 | collectionName: 'Rooms',
17 | typeName: 'Room',
18 | schema,
19 | resolvers: getDefaultResolvers('Rooms'),
20 | mutations: getDefaultMutations('Rooms'),
21 | });
22 |
23 | export default Rooms;
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/reviews/collection.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Reviews collection
4 |
5 | http://docs.vulcanjs.org/schemas.html
6 |
7 | */
8 |
9 | import { createCollection, getDefaultResolvers, getDefaultMutations } from 'meteor/vulcan:core';
10 | import schema from './schema.js';
11 | import './permissions.js';
12 | import { newCheck } from './check';
13 |
14 | const Reviews = createCollection({
15 | collectionName: 'Reviews',
16 | typeName: 'Review',
17 | schema,
18 | resolvers: getDefaultResolvers('Reviews'),
19 | mutations: getDefaultMutations('Reviews', { newCheck }),
20 | });
21 |
22 | export default Reviews;
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/rooms/RoomsNewPage.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Page for inserting a new room
4 |
5 | */
6 |
7 | import React from 'react';
8 | import { Components, registerComponent } from 'meteor/vulcan:core';
9 | import { FormattedMessage } from 'meteor/vulcan:i18n';
10 |
11 | // import RoomsNewForm from './RoomsNewForm';
12 |
13 | const RoomsNewPage = () =>
14 |
15 |
16 |
17 |
18 |
19 |
20 | registerComponent('RoomsNewPage', RoomsNewPage);
21 |
22 | // export default RoomsNewPage;
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/admin/AdminUsersRooms.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Show a user's rooms in the admin users dashboard
4 |
5 | http://docs.vulcanjs.org/admin.html
6 |
7 | */
8 |
9 | import React from 'react';
10 | import { Link } from 'react-router';
11 | import { registerComponent } from 'meteor/vulcan:core';
12 |
13 | const AdminUsersRooms = ({ document: user }) =>
14 |
15 | {user.rooms && user.rooms.map(room =>
16 | {room.name}
17 | )}
18 |
19 |
20 | registerComponent('AdminUsersRooms', AdminUsersRooms);
21 |
22 | export default AdminUsersRooms;
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_global.scss:
--------------------------------------------------------------------------------
1 | .main{
2 | padding: $spacing;
3 | max-width: 900px;
4 | margin: 0 auto;
5 | }
6 |
7 | .admin-main{
8 | padding: $spacing;
9 | width: 100%;
10 | }
11 |
12 | .page{
13 | }
14 |
15 | .page-title{
16 | margin-bottom: $spacing;
17 | padding-bottom: $spacing/2;
18 | border-bottom: 1px solid $lighter-grey;
19 | }
20 |
21 | .section-title{
22 | font-size: 1.5rem;
23 | margin-bottom: $spacing;
24 | padding-bottom: $spacing/2;
25 | border-bottom: 1px solid $lighter-grey;
26 | }
27 |
28 | .dropdown-item{
29 | &.active{
30 | a{
31 | color: white;
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_variables.scss:
--------------------------------------------------------------------------------
1 | $spacing: 20px;
2 |
3 | $smaller-font: 0.8rem;
4 | $small-font: 0.9rem;
5 | $medium-font: 1rem;
6 | $large-font: 1.25rem;
7 | $larger-font: 1.35rem;
8 |
9 | $border: 1px solid $light-border;
10 |
11 | @mixin activeHover{
12 | &:hover{
13 | background: $active-color;
14 | color: $white;
15 | border-color: $active-color;
16 | text-decoration: none;
17 | }
18 | }
19 |
20 | @mixin flex-center{
21 | display: flex;
22 | align-items: center;
23 | }
24 |
25 | @mixin border-radius{
26 | border-radius: .25rem;
27 | }
28 | @mixin border{
29 | border: $border;
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | - **All PRs should be made to the `devel` branch, not `master`.**
2 |
3 | - Come check-in in the [Telescope Slack channel](http://slack.telescopeapp.org/). 👋
4 |
5 | - Completely new features should be shipped as external packages with their own repos (see [3rd party packages](http://nova-docs.telescopeapp.org/plugins.html)). Don't hesitate to come by the [Slack channel](http://slack.telescopeapp.org/) to speak about it.
6 |
7 | - We don't have test at the moment, and Travis integration is broken. If you know how to fix it, you are welcome (see [#1253](https://github.com/TelescopeJS/Telescope/issues/1253)!
8 |
9 | - Be nice 😉
10 |
--------------------------------------------------------------------------------
/.meteor/.finished-upgraders:
--------------------------------------------------------------------------------
1 | # This file contains information which helps Meteor properly upgrade your
2 | # app when you run 'meteor update'. You should check it into version control
3 | # with your project.
4 |
5 | notices-for-0.9.0
6 | notices-for-0.9.1
7 | 0.9.4-platform-file
8 | notices-for-facebook-graph-api-2
9 | 1.2.0-standard-minifiers-package
10 | 1.2.0-meteor-platform-split
11 | 1.2.0-cordova-changes
12 | 1.2.0-breaking-changes
13 | 1.3.0-split-minifiers-package
14 | 1.3.5-remove-old-dev-bundle-link
15 | 1.4.0-remove-old-dev-bundle-link
16 | 1.4.1-add-shell-server-package
17 | 1.4.3-split-account-service-packages
18 | 1.5-add-dynamic-import-package
19 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/admin/AdminUsersReviews.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Show a user's reviews in the admin users dashboard
4 |
5 | http://docs.vulcanjs.org/admin.html
6 |
7 | */
8 |
9 | import React from 'react';
10 | import { Link } from 'react-router';
11 | import { registerComponent, Utils } from 'meteor/vulcan:core';
12 |
13 | const AdminUsersReviews = ({ document: user }) =>
14 |
15 | {user.reviews && user.reviews.map(review =>
16 | {Utils.trimWords(review.comment, 15)}
17 | )}
18 |
19 |
20 | registerComponent('AdminUsersReviews', AdminUsersReviews);
21 |
22 | export default AdminUsersReviews;
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/fragments.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Fragments on the Users collection
4 |
5 | http://docs.vulcanjs.org/fragments.html
6 |
7 | */
8 |
9 | import { extendFragment } from 'meteor/vulcan:core';
10 |
11 | extendFragment('UsersAdmin', `
12 | rooms(limit: 5){
13 | ...RoomsItemFragment
14 | }
15 | bookings(limit: 5){
16 | ...BookingsItemFragment
17 | }
18 | reviews(limit: 5){
19 | ...ReviewsDefaultFragment
20 | }
21 | `);
22 |
23 | extendFragment('UsersCurrent', `
24 | rooms(limit: 5){
25 | ...RoomsItemFragment
26 | }
27 | bookings(limit: 5){
28 | ...BookingsItemFragment
29 | }
30 | reviews(limit: 5){
31 | ...ReviewsDefaultFragment
32 | }
33 | `);
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/products.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Products
4 |
5 | http://docs.vulcanjs.org/payments.html
6 |
7 | */
8 |
9 | import { addProduct } from 'meteor/vulcan:payments';
10 | import moment from 'moment';
11 | import Rooms from './rooms/collection';
12 |
13 | addProduct('booking', booking => {
14 |
15 | const numberOfNights = moment(booking.endAt).diff(moment(booking.startAt), 'days');
16 | const room = booking.room || Rooms.findOne({_id: booking.roomId});
17 | const amount = room.pricePerNight * booking.numberOfGuests * numberOfNights * 100;
18 |
19 | return {
20 | name: 'Book this room',
21 | amount,
22 | currency: 'USD',
23 | description: `Book ${room.name}`,
24 | }
25 | });
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/admin/AdminUsersBookings.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Show a user's bookings in the admin users dashboard
4 |
5 | http://docs.vulcanjs.org/admin.html
6 |
7 | */
8 |
9 | import React from 'react';
10 | import { Link } from 'react-router';
11 | import { registerComponent } from 'meteor/vulcan:core';
12 |
13 | const AdminUsersBookings = ({ document: user }) =>
14 |
15 | {user.bookings && user.bookings.map(booking =>
16 | {booking.startAtFormattedShort} - {booking.endAtFormattedShort}
17 | )}
18 |
19 |
20 | registerComponent('AdminUsersBookings', AdminUsersBookings);
21 |
22 | export default AdminUsersBookings;
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_users.scss:
--------------------------------------------------------------------------------
1 | .accounts-ui{
2 | .field{
3 | margin-bottom: $spacing/2;
4 | }
5 | }
6 |
7 | .users-menu{
8 | .avatar{
9 | margin-right: 5px;
10 | }
11 | }
12 |
13 | .avatar-initials{
14 | background: $light-grey;
15 | color: $dark-grey;
16 | text-align: center;
17 | border-radius: 100%;
18 | text-decoration: none;
19 | height: 100%;
20 | display: flex;
21 | justify-content: center;
22 | align-items: center;
23 | span{
24 | display: block;
25 | line-height: 1;
26 | }
27 | }
28 |
29 | .users-newsletter-settings {
30 | @include flex-center;
31 | margin-top: $spacing;
32 | }
33 |
34 | .log-in-message{
35 | h3{
36 | text-align: center;
37 | }
38 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .eslintcache
2 | npm-debug.log
3 | *.scssc
4 | .sass-cache/*
5 | .DS_Store
6 | *-ck.js
7 | providers_secret.js
8 | *.sublime-project
9 |
10 | *.sublime-workspace
11 | codekit-config.json
12 |
13 | config.rb
14 |
15 | deploy.sh
16 |
17 | .demeteorized
18 |
19 | dump/
20 | dump/*
21 |
22 | settings.json
23 | settings.json.not-used
24 | .idea
25 |
26 | scratch
27 |
28 | .deploy
29 | .deploy/*
30 | ### Meteor template
31 | .meteor/local
32 | .meteor/meteorite
33 | mup.json
34 |
35 | packages_update.py
36 |
37 | versions
38 |
39 | get_file_list.sh
40 | packages_update.py
41 | publish_packages.sh
42 |
43 | node_modules
44 |
45 | bundle.tar.gz
46 |
47 | jsdoc-conf.json
48 | jsdoc.json
49 | packages/nova-router/.npm
50 | npm-debug.log
51 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/rooms/RoomsReviews.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Show a room's reviews
4 |
5 | */
6 |
7 | import React from 'react';
8 | import { Components, registerComponent } from 'meteor/vulcan:core';
9 | import { FormattedMessage } from 'meteor/vulcan:i18n';
10 |
11 | // import ReviewsList from '../reviews/ReviewsList';
12 | // import ReviewsNewForm from '../reviews/ReviewsNewForm';
13 |
14 | const RoomsReviews = ({ room, currentUser }) =>
15 |
16 |
17 |
18 |
19 | {currentUser ? : null}
20 |
21 |
22 |
23 | registerComponent('RoomsReviews', RoomsReviews);
24 |
25 | // export default RoomsReviews;
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/admin.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Extend the users admin dashboard
4 |
5 | http://docs.vulcanjs.org/admin.html
6 |
7 | */
8 |
9 | import { extendFragment, addAdminColumn } from 'meteor/vulcan:core';
10 | import AdminUsersRooms from '../components/admin/AdminUsersRooms';
11 | import AdminUsersBookings from '../components/admin/AdminUsersBookings';
12 | import AdminUsersReviews from '../components/admin/AdminUsersReviews';
13 |
14 | addAdminColumn({
15 | name: 'rooms',
16 | order: 50,
17 | component: AdminUsersRooms
18 | });
19 |
20 | addAdminColumn({
21 | name: 'bookings',
22 | order: 60,
23 | component: AdminUsersBookings
24 | });
25 |
26 | addAdminColumn({
27 | name: 'reviews',
28 | order: 70,
29 | component: AdminUsersReviews
30 | });
31 |
32 |
--------------------------------------------------------------------------------
/packages/vulcan-maps/lib/server/maps.js:
--------------------------------------------------------------------------------
1 | import googleMaps from '@google/maps';
2 | import { getSetting } from 'meteor/vulcan:core';
3 | import Promise from 'bluebird';
4 |
5 | const googleMapsSetting = getSetting('googlemaps');
6 |
7 | if (!googleMapsSetting) {
8 | throw new Error('Please fill in your Google Maps API Key or disable the Maps package.');
9 | }
10 |
11 | const googleMapsClient = googleMaps.createClient({
12 | key: googleMapsSetting.apiKey
13 | });
14 |
15 | /*
16 |
17 | Note: Google Maps Node package doesn't support promises natively.
18 | See https://github.com/google/google-api-nodejs-client/issues/197#issuecomment-299569914
19 |
20 | */
21 | export async function geocode(address) {
22 | const data = await Promise.fromCallback((cb) => googleMapsClient.geocode({ address }, cb));
23 | return data.json.results[0];
24 | }
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/common/FlashMessages.js:
--------------------------------------------------------------------------------
1 | // /*
2 |
3 | // Show all flash messages
4 |
5 | // */
6 |
7 | // import { Components, registerComponent, withMessages } from 'meteor/vulcan:core';
8 | // import React from 'react';
9 |
10 | // // import Flash from './Flash';
11 |
12 | // const FlashMessages = ({messages, clear, markAsSeen}) => {
13 | // return (
14 | //
15 | // {messages
16 | // .filter(message => message.show)
17 | // .map(message => )}
18 | //
19 | // );
20 | // }
21 |
22 | // FlashMessages.displayName = "FlashMessages";
23 |
24 | // registerComponent('FlashMessages', FlashMessages, withMessages);
25 |
26 | // export default withMessages(FlashMessages);
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/users/UsersSignUp.jsx:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Users sign up form
4 |
5 | */
6 |
7 | import { Components, registerComponent } from 'meteor/vulcan:core';
8 | import React, { PropTypes, Component } from 'react';
9 | import { FormattedMessage } from 'meteor/vulcan:i18n';
10 | import { STATES } from 'meteor/vulcan:accounts';
11 | import { Link } from 'react-router';
12 |
13 | const UsersSignUp = ({state}) =>
14 |
15 |
19 |
20 | UsersSignUp.displayName = 'UsersSignUp';
21 |
22 | registerComponent('UsersSignUp', UsersSignUp);
23 |
24 | // export default UsersSignUp;
25 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/admin/ReviewsDashboard.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Show a list of all reviews
4 |
5 | http://docs.vulcanjs.org/core-components.html#Datatable
6 |
7 | */
8 |
9 | import React from 'react';
10 | import { Components, registerComponent } from 'meteor/vulcan:core';
11 | import compose from 'recompose/compose';
12 | import { FormattedMessage } from 'meteor/vulcan:i18n';
13 |
14 | import Reviews from '../../modules/reviews/collection.js';
15 |
16 | const ReviewsDashboard = () =>
17 |
18 |
19 |
20 |
21 |
22 |
27 |
28 |
29 |
30 | registerComponent('ReviewsDashboard', ReviewsDashboard);
31 |
32 | // export default ReviewsDashboard;
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/users/UsersLogIn.jsx:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Users log in form
4 |
5 | */
6 |
7 | import { Components, registerComponent } from 'meteor/vulcan:core';
8 | import React, { PropTypes, Component } from 'react';
9 | import { FormattedMessage } from 'meteor/vulcan:i18n';
10 | import Dropdown from 'react-bootstrap/lib/Dropdown';
11 | import { STATES } from 'meteor/vulcan:accounts';
12 | import { Link } from 'react-router';
13 |
14 | const UsersLogIn = ({state}) =>
15 |
16 |
20 |
21 | UsersLogIn.displayName = 'UsersLogIn';
22 |
23 | registerComponent('UsersLogIn', UsersLogIn);
24 |
25 | // export default UsersLogIn;
26 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/bookings/collection.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Bookings collection
4 |
5 | http://docs.vulcanjs.org/schemas.html
6 |
7 | */
8 |
9 | import { createCollection, getDefaultResolvers, getDefaultMutations } from 'meteor/vulcan:core';
10 | import Users from 'meteor/vulcan:users';
11 | import schema from './schema.js';
12 | import './fragments.js';
13 | import './permissions.js';
14 | import './resolvers.js';
15 |
16 | const Bookings = createCollection({
17 | collectionName: 'Bookings',
18 | typeName: 'Booking',
19 | schema,
20 | resolvers: getDefaultResolvers('Bookings'),
21 | mutations: getDefaultMutations('Bookings'),
22 | });
23 |
24 | // only admins or the owner can access a booking
25 | Bookings.checkAccess = (currentUser, booking) => {
26 | return Users.isAdmin(currentUser) || Users.owns(currentUser, booking);
27 | }
28 |
29 | export default Bookings;
--------------------------------------------------------------------------------
/packages/zensroom/package.js:
--------------------------------------------------------------------------------
1 | Package.describe({
2 | name: 'zensroom',
3 | });
4 |
5 | Package.onUse((api) => {
6 | api.use([
7 |
8 | 'fourseven:scss@4.5.0',
9 |
10 | // vulcan core
11 | 'vulcan:core',
12 |
13 | // vulcan packages
14 | 'vulcan:forms',
15 | 'vulcan:forms-upload',
16 | 'vulcan:accounts',
17 | 'vulcan:payments',
18 | 'vulcan:maps',
19 | 'vulcan:admin',
20 |
21 | ]);
22 |
23 | api.addFiles(['lib/stylesheets/main.scss'], ['client']);
24 |
25 | api.addAssets([
26 | 'lib/server/emails/common/test.handlebars',
27 | 'lib/server/emails/common/wrapper.handlebars',
28 | 'lib/server/emails/rooms/roomsNew.handlebars',
29 | 'lib/server/emails/bookings/bookingsNew.handlebars',
30 | ], ['server']);
31 |
32 | api.mainModule('lib/server/main.js', 'server');
33 | api.mainModule('lib/client/main.js', 'client');
34 | });
--------------------------------------------------------------------------------
/packages/zensroom/lib/containers/withUnavailableDatesContainer.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Container for the UnavailableDates query
4 |
5 | http://docs.vulcanjs.org/data-loading.html#Higher-Order-Components
6 |
7 | */
8 |
9 | import { graphql } from 'react-apollo';
10 | import gql from 'graphql-tag';
11 |
12 | const query = gql`
13 | query unavailableDates($roomId: String) {
14 | UnavailableDates(roomId:$roomId)
15 | }
16 | `
17 |
18 | const withUnavailableDates = graphql(query, {
19 |
20 | alias: 'withUnavailableDates',
21 |
22 | options({room}) {
23 | return {
24 | variables: {
25 | roomId: room._id,
26 | },
27 | };
28 | },
29 |
30 | props({ data }) {
31 | return {
32 | loading: data.networkStatus === 1,
33 | unavailableDates: data.UnavailableDates
34 | }
35 | }
36 |
37 | });
38 |
39 | export default withUnavailableDates;
40 |
41 |
42 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/bookings/fragments.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Fragments on the Bookings collection
4 |
5 | http://docs.vulcanjs.org/fragments.html
6 |
7 | */
8 |
9 | import { registerFragment } from 'meteor/vulcan:core';
10 |
11 | registerFragment(`
12 | fragment BookingsItemFragment on Booking {
13 |
14 | __typename
15 | _id
16 | createdAt
17 | startAt
18 | endAt
19 | paidAt
20 | startAtFormatted
21 | endAtFormatted
22 | paidAtFormatted
23 | startAtFormattedShort
24 | endAtFormattedShort
25 | paidAtFormattedShort
26 | numberOfGuests
27 | status
28 | amount
29 | pageUrl
30 |
31 | user {
32 | _id
33 | displayName
34 | emailHash
35 | slug
36 | pageUrl
37 | }
38 |
39 | room {
40 | _id
41 | pricePerNight
42 | name
43 | photos
44 | pageUrl
45 | }
46 |
47 | }
48 | `);
49 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/reviews/schema.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Reviews schema
4 |
5 | http://docs.vulcanjs.org/schemas.html#Schemas
6 |
7 | */
8 |
9 | const schema = {
10 | // default properties
11 |
12 | _id: {
13 | type: String,
14 | optional: true,
15 | viewableBy: ['guests'],
16 | },
17 | createdAt: {
18 | type: Date,
19 | optional: true,
20 | viewableBy: ['guests'],
21 | onInsert: (document, currentUser) => {
22 | return new Date();
23 | }
24 | },
25 | userId: {
26 | type: String,
27 | optional: true,
28 | viewableBy: ['guests'],
29 | },
30 |
31 | roomId: {
32 | type: String,
33 | viewableBy: ['guests'],
34 | insertableBy: ['members'],
35 | hidden: true,
36 | },
37 |
38 | comment: {
39 | label: 'Comment',
40 | type: String,
41 | optional: false,
42 | viewableBy: ['guests'],
43 | insertableBy: ['members'],
44 | editableBy: ['members'],
45 | control: 'textarea'
46 | },
47 |
48 | };
49 |
50 | export default schema;
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/users/UsersAccountMenu.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | User menu (when not logged in)
4 |
5 | */
6 |
7 | import { Components, registerComponent } from 'meteor/vulcan:core';
8 | import React, { PropTypes, Component } from 'react';
9 | import { FormattedMessage } from 'meteor/vulcan:i18n';
10 | import Dropdown from 'react-bootstrap/lib/Dropdown';
11 | import { STATES } from 'meteor/vulcan:accounts';
12 |
13 | const UsersAccountMenu = ({state}) =>
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | UsersAccountMenu.displayName = "UsersAccountMenu";
26 |
27 | registerComponent('UsersAccountMenu', UsersAccountMenu);
28 |
29 | // export default UsersAccountMenu;
30 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/bookings/resolvers.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Resolver to get the list of unavailable dates for a given room
4 |
5 | http://docs.vulcanjs.org/data-loading.html#Custom-Queries
6 |
7 | */
8 |
9 | import { addGraphQLResolvers, addGraphQLQuery } from 'meteor/vulcan:core';
10 | import moment from 'moment';
11 |
12 | const unavailableDatesResolver = {
13 | Query: {
14 | UnavailableDates (root, { roomId }, { Rooms, Bookings, currentUser }) {
15 | const bookings = Bookings.find({ roomId, paidAt: {$exists: true} }).fetch();
16 | let dates = [];
17 | bookings.forEach(booking => {
18 | const mStart = moment(booking.startAt);
19 | const mEnd = moment(booking.endAt);
20 | const duration = moment.duration(mEnd.diff(mStart)).as('days');
21 | for(i = 0; i <= duration; i++) {
22 | dates.push(mStart.add(1, 'days').toDate())
23 | }
24 | });
25 | return dates;
26 | }
27 | }
28 | };
29 | addGraphQLResolvers(unavailableDatesResolver);
30 |
31 | addGraphQLQuery(`UnavailableDates(roomId: String): [Date]`);
32 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/bookings/views.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Bookings views
4 |
5 | http://docs.vulcanjs.org/terms-parameters.html#Using-Views
6 |
7 | */
8 |
9 | import Bookings from './collection.js';
10 | import moment from 'moment';
11 |
12 | Bookings.addView('userBookings', terms => ({
13 | selector: {
14 | userId: terms.userId,
15 | roomId: terms.roomId,
16 | paidAt: {$exists: true}
17 | }
18 | }));
19 |
20 | Bookings.addView('userPendingBookings', terms => ({
21 | selector: {
22 | userId: terms.userId,
23 | status: 1
24 | }
25 | }));
26 |
27 | Bookings.addView('userBookingsPast', terms => ({
28 | selector: {
29 | userId: terms.userId,
30 | endAt: {$lt: new Date()}
31 | }
32 | }));
33 |
34 | Bookings.addView('userBookingsCurrent', terms => ({
35 | selector: {
36 | userId: terms.userId,
37 | startAt: {$lt: new Date()},
38 | endAt: {$gt: new Date()}
39 | }
40 | }));
41 |
42 | Bookings.addView('userBookingsFuture', terms => ({
43 | selector: {
44 | userId: terms.userId,
45 | startAt: {$gt: new Date()}
46 | }
47 | }));
48 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/reviews/check.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Check if a user can perform the "new" mutation
4 |
5 | http://docs.vulcanjs.org/mutations.html#New-Mutation
6 |
7 | */
8 |
9 | import moment from 'moment';
10 | import Users from 'meteor/vulcan:users';
11 |
12 | export const newCheck = (user, document) => {
13 | // if user is not logged in, disallow operation
14 | if (!user) return false;
15 |
16 | /*
17 |
18 | else, check if:
19 |
20 | - they can perform the "reviews.new" operation
21 | - they have booked the associated room in the past
22 | - they haven't already left a review
23 |
24 | */
25 |
26 | const hasBookedRoom = _.some(user.bookings, booking => booking.roomId === document.roomId && moment().isBefore(moment(booking.endAt)));
27 | const hasLeftAReview = _.some(user.reviews, review => review.roomId === document.roomId);
28 |
29 | // console.log(user)
30 | // console.log(document)
31 | // console.log(hasBookedRoom)
32 | // console.log(hasLeftAReview)
33 |
34 | return Users.canDo(user, `reviews.new`) && hasBookedRoom && !hasLeftAReview;
35 | }
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/bookings/BookingsPending.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Components, registerComponent, withList, withCurrentUser, withDocument } from 'meteor/vulcan:core';
3 | import { Link } from 'react-router';
4 | import { Alert } from 'react-bootstrap';
5 | import compose from 'recompose/compose';
6 |
7 | import Bookings from '../../modules/bookings/collection.js';
8 |
9 | const BookingsPending = ({loading, results}) =>
10 |
11 | {results && results.length ?
12 |
13 | {results.map((booking) => (
14 |
15 | Complete your booking of {booking.room.name}.
16 |
17 | ))}
18 | :
19 | null
20 | }
21 |
;
22 |
23 |
24 | const options = {
25 | collection: Bookings,
26 | fragmentName: 'BookingsItemFragment'
27 | };
28 |
29 | registerComponent('BookingsPending', BookingsPending, [withList, options]);
30 |
31 | // export default compose(
32 | // withList(options),
33 | // )(BookingsPending);
34 |
--------------------------------------------------------------------------------
/license.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Telescope Nova
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/admin/RoomsDashboard.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Show a list of all rooms
4 |
5 | http://docs.vulcanjs.org/core-components.html#Datatable
6 |
7 | */
8 |
9 | import React from 'react';
10 | import { Components, registerComponent } from 'meteor/vulcan:core';
11 | import compose from 'recompose/compose';
12 | import { FormattedMessage } from 'meteor/vulcan:i18n';
13 |
14 | import Rooms from '../../modules/rooms/collection.js';
15 |
16 | const RoomPhotos = ({ document }) =>
17 |
18 | {document.photos.map((photo) =>
)}
19 |
;
20 |
21 | const RoomsDashboard = () =>
22 |
23 |
24 |
25 |
26 |
27 |
39 |
40 |
41 |
42 | registerComponent('RoomsDashboard', RoomsDashboard);
43 |
44 | // export default RoomsDashboard;
45 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/rooms/RoomsItem.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Room list item
4 |
5 | */
6 |
7 | import React from 'react';
8 | import { Components, registerComponent, getSetting } from 'meteor/vulcan:core';
9 | import { Link } from 'react-router';
10 | import { FormattedMessage } from 'meteor/vulcan:i18n';
11 |
12 | const RoomsItem = ({room, currentUser}) =>
13 |
14 |
15 |
16 |
17 |
18 | {room.photos && room.photos.length ?
: null}
19 |
{getSetting('defaultCurrency', '$')}{room.pricePerNight}
20 |
21 |
22 |
23 |
24 |
25 |
{room.name}
26 | {room.city}
27 |
28 |
29 |
30 |
31 |
32 | registerComponent('RoomsItem', RoomsItem);
33 |
34 | // export default RoomsItem;
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_admin.scss:
--------------------------------------------------------------------------------
1 | .datatable{
2 | .avatar{
3 | display: inline-block;
4 | width: 20px;
5 | height: 20px;
6 | margin-right: 5px;
7 | vertical-align: middle;
8 | a, img{
9 | border-radius: 100%;
10 | display: block;
11 | width: 100%;
12 | }
13 | }
14 | }
15 |
16 | .datatable-search{
17 | margin-bottom: $spacing/2;
18 | max-width: 200px;
19 | }
20 |
21 | //////////////////////////////////////////////////////
22 | // Bookings //
23 | //////////////////////////////////////////////////////
24 |
25 | .bookings-dashboard-room{
26 | img{
27 | display: inline-block;
28 | max-width: 60px;
29 | margin-right: 10px;
30 | }
31 | span{
32 |
33 | }
34 | }
35 |
36 | //////////////////////////////////////////////////////
37 | // Photos //
38 | //////////////////////////////////////////////////////
39 |
40 | .datatable-item-photos, .datatable-item-Photos{
41 | min-width: 300px;
42 | img{
43 | display: inline-block;
44 | max-width: 60px;
45 | margin-right: 10px;
46 | margin-bottom: 10px;
47 | }
48 | }
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/bookings/BookingsFuture.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core';
3 | import { FormattedMessage } from 'meteor/vulcan:i18n';
4 | import compose from 'recompose/compose';
5 | import { Link } from 'react-router';
6 |
7 | import Bookings from '../../modules/bookings/collection.js';
8 |
9 | const BookingsDate = ({ document }) =>
10 |
11 | From {document.startAtFormattedShort} to {document.endAtFormattedShort}
12 |
13 |
14 | const BookingsFuture = ({ currentUser }) =>
15 |
16 |
31 |
;
32 |
33 |
34 | registerComponent('BookingsFuture', BookingsFuture, withCurrentUser);
35 |
36 | // export default compose(
37 | // withCurrentUser,
38 | // )(BookingsFuture);
39 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/bookings/BookingsCurrent.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core';
3 | import { FormattedMessage } from 'meteor/vulcan:i18n';
4 | import compose from 'recompose/compose';
5 | import { Link } from 'react-router';
6 |
7 | import Bookings from '../../modules/bookings/collection.js';
8 |
9 | const BookingsDate = ({ document }) =>
10 |
11 | From {document.startAtFormattedShort} to {document.endAtFormattedShort}
12 |
13 |
14 | const BookingsCurrent = ({ currentUser }) =>
15 |
16 |
31 |
;
32 |
33 |
34 | registerComponent('BookingsCurrent', BookingsCurrent, withCurrentUser);
35 |
36 | // export default compose(
37 | // withCurrentUser,
38 | // )(BookingsCurrent);
39 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_spinner.scss:
--------------------------------------------------------------------------------
1 |
2 | .spinner {
3 | height: 10px;
4 | @include flex-center;
5 | justify-content: center;
6 | div {
7 | width: 10px;
8 | height: 10px;
9 | background-color: rgba(0,0,0,0.35);
10 |
11 | border-radius: 100%;
12 | display: inline-block;
13 | -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
14 | animation: sk-bouncedelay 1.4s infinite ease-in-out both;
15 | margin-right: $spacing/2;
16 | &:last-child{
17 | margin-right: 0;
18 | }
19 | }
20 | &.white div{
21 | background-color: rgba(255,255,255,0.55);
22 | }
23 | .bounce1 {
24 | -webkit-animation-delay: -0.32s;
25 | animation-delay: -0.32s;
26 | }
27 |
28 | .bounce2 {
29 | -webkit-animation-delay: -0.16s;
30 | animation-delay: -0.16s;
31 | }
32 | }
33 |
34 | @-webkit-keyframes sk-bouncedelay {
35 | 0%, 80%, 100% { -webkit-transform: scale(0) }
36 | 40% { -webkit-transform: scale(1.0) }
37 | }
38 |
39 | @keyframes sk-bouncedelay {
40 | 0%, 80%, 100% {
41 | -webkit-transform: scale(0);
42 | transform: scale(0);
43 | } 40% {
44 | -webkit-transform: scale(1.0);
45 | transform: scale(1.0);
46 | }
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/bookings/BookingsPast.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core';
3 | import { FormattedMessage } from 'meteor/vulcan:i18n';
4 | import compose from 'recompose/compose';
5 | import { Link } from 'react-router';
6 |
7 | import Bookings from '../../modules/bookings/collection.js';
8 |
9 | const BookingsDate = ({ document }) =>
10 |
11 | From {document.startAtFormattedShort} to {document.endAtFormattedShort}
12 |
13 |
14 | const BookingsPast = ({currentUser}) =>
15 |
16 |
31 |
;
32 |
33 |
34 | registerComponent('BookingsPast', BookingsPast, withCurrentUser);
35 |
36 | // export default compose(
37 | // withCurrentUser,
38 | // )(BookingsPast);
39 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/common/Home.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Home
4 |
5 | */
6 |
7 | import React from 'react';
8 | import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core';
9 | import { FormattedMessage } from 'meteor/vulcan:i18n';
10 | import compose from 'recompose/compose';
11 |
12 |
13 | // import RoomsList from '../rooms/RoomsList';
14 | // import RoomsSearchForm from '../rooms/RoomsSearchForm';
15 |
16 | const Home = ({currentUser}) =>
17 |
18 |
19 | {currentUser ?
: null}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | registerComponent('Home', Home, withCurrentUser);
35 |
36 | // export default compose(
37 | // withCurrentUser,
38 | // )(Home);
39 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_header.scss:
--------------------------------------------------------------------------------
1 | .header{
2 | @include flex-center;
3 | justify-content: space-between;
4 | padding: $spacing;
5 | border-bottom: 1px solid #eee;
6 | margin-bottom: $spacing;
7 | }
8 |
9 | .logo{
10 | display: inline-block;
11 | margin-right: 5px;
12 | h1{
13 | font-size: 2rem;
14 | }
15 | }
16 |
17 | .tagline{
18 | display: inline-block;
19 | }
20 |
21 | .nav{
22 | @include flex-center;
23 | }
24 |
25 | .nav-item{
26 | margin-right: $spacing;
27 | &:last-child{
28 | margin: 0;
29 | }
30 | }
31 |
32 |
33 | .users-account-menu, .users-menu{
34 | .avatar{
35 | width: 20px;
36 | height: 20px;
37 | img{
38 | display: block;
39 | width: 100%;
40 | height: 100%;
41 | }
42 | }
43 | .dropdown-toggle{
44 | @include flex-center;
45 | padding: 0;
46 | background: none;
47 | img{
48 | margin-right: 5px;
49 | }
50 | }
51 | }
52 |
53 | .accounts-ui{
54 | padding: $spacing;
55 | min-width: 240px;
56 | .buttons{
57 | .btn{
58 | display: block;
59 | width: 100%;
60 | text-align: center;
61 | margin-bottom: $spacing;
62 | &:last-child{
63 | margin-bottom: 0;
64 | }
65 | }
66 | }
67 | }
--------------------------------------------------------------------------------
/packages/vulcan-maps/lib/components/Map.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Components, registerComponent, getSetting } from 'meteor/vulcan:core';
4 | import GoogleMap from 'google-map-react';
5 |
6 | const Marker = () => (
7 |
8 | );
9 |
10 | class Map extends Component {
11 |
12 | static defaultProps = {
13 | zoom: 15
14 | };
15 |
16 | constructor(props) {
17 | super(props);
18 | }
19 |
20 | render() {
21 | return (
22 |
23 |
31 | {this.props.coordinates.map((coord, index) => )}
32 |
33 |
34 |
35 | )
36 | }
37 | }
38 |
39 | Map.propTypes = {
40 | zoom: PropTypes.number,
41 | }
42 |
43 | registerComponent('Map', Map);
44 |
--------------------------------------------------------------------------------
/jsdoc.json:
--------------------------------------------------------------------------------
1 | {
2 | // Execute meteor-jsdoc in debug mode to track down errors.
3 | "debug": true,
4 | // node.js install path, default to: "`which node`" on Mac and Linux, "`where node`" on Windows
5 | "nodePath": "",
6 | // Project docs path
7 | "docsPath": "~/Dev/nova-docs",
8 | // Project docs Meteor server port, default to: 3333
9 | "meteorPort": 3333,
10 | // Copy the Meteor docs server before building the docs (required for the first build)
11 | // Setting this to false after the first build allows you to customize the docs templates
12 | // without seeing your changes overwritten the next time you build the docs.
13 | "initMeteor": true,
14 | // Update Meteor without overwriting your changes to the docs templates.
15 | "updateMeteor": true,
16 | // Add a preamble to your project's docs that will appear at the top of the docs.
17 | "preamble": true,
18 | // Link to the project repository (used to construct the file path in the docs). Optional.
19 | "projectRepo": "https://github.com/TelescopeJS/Telescope/tree/nova",
20 | // Values to be used in the `` for the docs.
21 | "docsConfig": {
22 | "title": "Nova Docs",
23 | "metas": {
24 | "description": "Documentation for Telescope Nova."
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_accounts.scss:
--------------------------------------------------------------------------------
1 | .users-account-menu{
2 | .icon{
3 | display: none;
4 | }
5 | }
6 |
7 | .accounts-ui{
8 | .buttons{
9 | a{
10 | display:block;
11 | text-align: center;
12 | margin-bottom: $spacing;
13 | }
14 | }
15 | .password-or-service{
16 | text-align: center;
17 | margin-bottom: $spacing;
18 | }
19 | .social-buttons{
20 | @include flex-center;
21 | justify-content: space-between;
22 | button{
23 | margin-right: $spacing;
24 | width: 100%;
25 | &.btn-twitter{
26 | background: #55acee;
27 | border-color: #55acee;
28 | }
29 | &.btn-facebook{
30 | background: #3b5998;
31 | border-color: #3b5998;
32 | }
33 | &:last-child{
34 | margin-right: 0 !important;
35 | }
36 | }
37 | }
38 | input.form-control{
39 | font-size: 0.9rem;
40 | }
41 | .warning, .error{
42 | color: #a94442;
43 | background: #f2dede;
44 | border: 1px solid #ebcccc;
45 | padding: $spacing/4 $spacing/2;
46 | border-radius: 3px;
47 | display: block;
48 | margin-top: $spacing/4;
49 | }
50 | }
51 |
52 | .password-reset-form{
53 | text-align: center;
54 | }
55 |
56 | .change-password-link{
57 | margin-bottom: $spacing;
58 | }
59 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/common/Flash.js:
--------------------------------------------------------------------------------
1 | // /*
2 |
3 | // Single Flash message.
4 |
5 | // */
6 |
7 | // import { registerComponent } from 'meteor/vulcan:core';
8 | // import React, { PureComponent } from 'react';
9 | // import PropTypes from 'prop-types';
10 | // import Alert from 'react-bootstrap/lib/Alert'
11 |
12 | // class Flash extends PureComponent {
13 |
14 | // constructor() {
15 | // super();
16 | // this.dismissFlash = this.dismissFlash.bind(this);
17 | // }
18 |
19 | // componentDidMount() {
20 | // this.props.markAsSeen(this.props.message._id);
21 | // }
22 |
23 | // dismissFlash(e) {
24 | // e.preventDefault();
25 | // this.props.clear(this.props.message._id);
26 | // }
27 |
28 | // render() {
29 |
30 | // let flashType = this.props.message.flashType;
31 | // flashType = flashType === "error" ? "danger" : flashType; // if flashType is "error", use "danger" instead
32 |
33 | // return (
34 | //
35 | // {this.props.message.content}
36 | //
37 | // )
38 | // }
39 | // }
40 |
41 | // Flash.propTypes = {
42 | // message: PropTypes.object.isRequired
43 | // }
44 |
45 | // registerComponent('Flash', Flash);
46 |
47 | // export default Flash;
48 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/common/Layout.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Layout
4 |
5 | */
6 |
7 | import { Components, replaceComponent, withCurrentUser } from 'meteor/vulcan:core';
8 | import React, { PropTypes, Component } from 'react';
9 | import classNames from 'classnames';
10 | import Helmet from 'react-helmet';
11 |
12 | // import Header from './Header';
13 | // import FlashMessages from './FlashMessages';
14 | // import Footer from './Footer';
15 |
16 | const Layout = ({currentUser, children, currentRoute}) =>
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | {React.cloneElement(children, { currentUser })}
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | replaceComponent('Layout', Layout);
40 |
41 | // export default withCurrentUser(Layout);
42 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/common/AdminLayout.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Layout
4 |
5 | */
6 |
7 | import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core';
8 | import React, { PropTypes, Component } from 'react';
9 | import classNames from 'classnames';
10 | import Helmet from 'react-helmet';
11 |
12 | // import Header from './Header';
13 | // import FlashMessages from './FlashMessages';
14 | // import Footer from './Footer';
15 |
16 | const AdminLayout = ({currentUser, children, currentRoute}) =>
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | {React.cloneElement(children, { currentUser })}
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | registerComponent('AdminLayout', AdminLayout, withCurrentUser);
40 |
41 | // export default withCurrentUser(Layout);
42 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/reviews/ReviewsList.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | List of reviews, wrapped with withList
4 |
5 | http://docs.vulcanjs.org/data-loading.html#List-Resolver
6 |
7 | */
8 |
9 | import React from 'react';
10 | import { Components, registerComponent, withList, withCurrentUser, Loading } from 'meteor/vulcan:core';
11 | import { FormattedMessage } from 'meteor/vulcan:i18n';
12 |
13 | import Reviews from '../../modules/reviews/collection';
14 |
15 | import ReviewsItem from './ReviewsItem';
16 |
17 | const ReviewsList = ({results = [], currentUser, loading, loadMore, count, totalCount}) =>
18 |
19 |
20 |
21 | {loading ?
22 |
23 |
:
24 |
25 |
36 | }
37 |
38 |
39 |
40 | const options = {
41 | collection: Reviews
42 | };
43 |
44 | registerComponent('ReviewsList', ReviewsList, [withList, options], withCurrentUser);
45 |
46 | // export default withList(options)(withCurrentUser(ReviewsList));
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/rooms/RoomsList.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Room list, wrapped with withList
4 |
5 | http://docs.vulcanjs.org/data-loading.html#List-Resolver
6 |
7 | */
8 |
9 | import React from 'react';
10 | import { Components, registerComponent, withList, withCurrentUser } from 'meteor/vulcan:core';
11 | import { FormattedMessage } from 'meteor/vulcan:i18n';
12 |
13 | import Rooms from '../../modules/rooms/collection';
14 | // import RoomsItem from './RoomsItem';
15 |
16 | const RoomsList = ({results = [], currentUser, loading, loadMore, count, totalCount}) =>
17 |
18 |
19 |
20 | {loading ?
21 |
22 |
:
23 |
24 |
35 | }
36 |
37 |
38 |
39 | const options = {
40 | collection: Rooms
41 | };
42 |
43 | registerComponent('RoomsList', RoomsList, [withList, options], withCurrentUser);
44 |
45 | // export default withList(options)(withCurrentUser(RoomsList));
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/data.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Static data
4 |
5 | */
6 |
7 | export const amenities = [
8 | { value: 'essentials', label: 'Essentials (towels, bed sheets, etc.)' },
9 | { value: 'wifi', label: 'Wifi' },
10 | { value: 'pocket-wifi', label: 'Pocket Wifi' },
11 | { value: 'shampoo', label: 'Shampoo' },
12 | { value: 'closet-drawers', label: 'Closet/drawers' },
13 | { value: 'tv', label: 'TV' },
14 | { value: 'ac', label: 'Air Conditioning' },
15 | { value: 'breakfast', label: 'Breakfast, coffee, tea' },
16 | { value: 'desk', label: 'Desk/workspace' },
17 | { value: 'fireplace', label: 'Fireplace' },
18 | { value: 'iron', label: 'Iron' },
19 | { value: 'hair-dryer', label: 'Hair dryer' },
20 | { value: 'pets', label: 'Pets in the house' },
21 | { value: 'private-entrance', label: 'Private entrance' },
22 | { value: 'washing-machine', label: 'Washing Machine' },
23 | { value: 'internet', label: 'Internet' },
24 | ]
25 |
26 | export const spaces = [
27 | { value: 'living', label: 'Private living room' },
28 | { value: 'pool', label: 'Pool' },
29 | { value: 'kitchen', label: 'Kitchen' },
30 | { value: 'washer', label: 'Laundry - washer' },
31 | { value: 'dryer', label: 'Laundry - dryer' },
32 | { value: 'parking', label: 'Parking' },
33 | { value: 'elevator', label: 'Elevator' },
34 | { value: 'hot-tub', label: 'Hot tub' },
35 | { value: 'gym', label: 'Gym' },
36 | ]
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/reviews/ReviewsNewForm.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Form for inserting a new review
4 |
5 | http://docs.vulcanjs.org/forms.html
6 |
7 | */
8 |
9 | import React from 'react';
10 | import { Components, registerComponent, withCurrentUser, getFragment, withMessages } from 'meteor/vulcan:core';
11 | import compose from 'recompose/compose';
12 | import { intlShape, FormattedMessage } from 'meteor/vulcan:i18n';
13 |
14 | import Reviews from '../../modules/reviews/collection.js';
15 |
16 | const ReviewsNewForm = ({roomId, currentUser, router, flash}, {intl}) =>
17 |
18 |
19 | {Reviews.options.mutations.new.check(currentUser, { roomId }) ?
20 |
21 |
22 |
23 | {
28 | flash(intl.formatMessage({id: 'reviews.created'}), 'success');
29 | }}
30 | />
31 | :
32 | null
33 | }
34 |
35 |
36 | ReviewsNewForm.contextTypes = {
37 | intl: intlShape
38 | };
39 |
40 | registerComponent('ReviewsNewForm', ReviewsNewForm, withMessages, withCurrentUser);
41 |
42 | // export default compose(
43 | // withMessages,
44 | // withCurrentUser
45 | // )(ReviewsNewForm);
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/common/Header.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Header
4 |
5 | */
6 |
7 | import React from 'react';
8 | import PropTypes from 'prop-types';
9 | import { Components, registerComponent, withCurrentUser, getSetting } from 'meteor/vulcan:core';
10 | import { Link } from 'react-router';
11 | import Button from 'react-bootstrap/lib/Button';
12 | import { FormattedMessage } from 'meteor/vulcan:i18n';
13 |
14 | // import UsersMenu from '../users/UsersMenu';
15 | // import UsersAccountMenu from '../users/UsersAccountMenu';
16 |
17 | const Header = ({ currentUser }, context) =>
18 |
36 |
37 | Header.displayName = 'Header';
38 |
39 | Header.propTypes = {
40 | currentUser: PropTypes.object,
41 | };
42 |
43 | registerComponent('Header', Header, withCurrentUser);
44 |
45 | // export default withCurrentUser(Header);
46 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/rooms/RoomsNewForm.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Form for inserting a new room
4 |
5 | http://docs.vulcanjs.org/forms.html
6 |
7 | */
8 |
9 | import React from 'react';
10 | import { Components, registerComponent, withCurrentUser, getFragment, withMessages } from 'meteor/vulcan:core';
11 | import { withRouter } from 'react-router';
12 | import compose from 'recompose/compose';
13 | import { intlShape, FormattedMessage } from 'meteor/vulcan:i18n';
14 |
15 | import Rooms from '../../modules/rooms/collection.js';
16 |
17 | const RoomsNewForm = ({currentUser, router, flash}, {intl}) =>
18 |
19 |
20 |
21 | {Rooms.options.mutations.new.check(currentUser) ?
22 |
23 | {
27 | router.push({pathname: `/room/${room._id}`});
28 | flash(intl.formatMessage({id: 'rooms.created'}), 'success');
29 | }}
30 | />
31 |
:
32 | null
33 | }
34 |
35 |
36 |
37 | RoomsNewForm.contextTypes = {
38 | intl: intlShape
39 | };
40 |
41 | registerComponent('RoomsNewForm', RoomsNewForm, withRouter, withMessages, withCurrentUser);
42 |
43 | // export default compose(
44 | // withRouter,
45 | // withMessages,
46 | // withCurrentUser
47 | // )(RoomsNewForm);
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/bookings/BookingsRoomUser.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Show a user's bookings for a given room. Wrapped with withList.
4 |
5 | Example:
6 |
7 |
8 |
9 | http://docs.vulcanjs.org/data-loading.html#List-Resolver
10 |
11 | */
12 |
13 | import React from 'react';
14 | import { Components, registerComponent, withList, withCurrentUser } from 'meteor/vulcan:core';
15 | import compose from 'recompose/compose';
16 | import { FormattedMessage } from 'meteor/vulcan:i18n';
17 |
18 | import Bookings from '../../modules/bookings/collection.js';
19 |
20 | const BookingsRoomUser = ({loading, results }) =>
21 |
22 |
23 |
24 |
25 |
26 | {loading ?
:
27 |
28 |
29 |
30 | {results.length ?
: }
31 | {results.map(booking => )}
32 |
33 |
34 | }
35 |
36 |
37 |
38 | const options = {
39 | collection: Bookings
40 | }
41 |
42 | registerComponent('BookingsRoomUser', BookingsRoomUser, [withList, options]);
43 |
44 | // export default compose(
45 | // withList(options),
46 | // )(BookingsRoomUser);
--------------------------------------------------------------------------------
/prestart_vulcan.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if tput setaf 1 &> /dev/null; then
4 | purple=$(tput setaf 141)
5 | blue=$(tput setaf 153)
6 | bold=$(tput bold)
7 | reset=$(tput sgr0)
8 | else
9 | purple=""
10 | blue=""
11 | bold=""
12 | reset=""
13 | fi
14 |
15 | command -v meteor >/dev/null 2>&1 || {
16 | echo "Vulcan requires Meteor but it's not installed. Trying to Install..." >&2;
17 |
18 | if [ "$(uname)" == "Darwin" ]; then
19 | # Mac OS platform
20 | echo "🌋 ${bold}${purple}Good news you have a Mac and we will install it now! ${reset}";
21 | curl https://install.meteor.com/ | bash;
22 | elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
23 | # GNU/Linux platform
24 | echo "🌋 ${bold}${purple}Good news you are on GNU/Linux platform and we will install Meteor now! ${reset}";
25 | curl https://install.meteor.com/ | bash;
26 | elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW32_NT" ]; then
27 | # Windows NT platform
28 | echo "🌋 ${bold}${purple}Oh no! you are on a Windows platform and you will need to install Meteor Manually! ${reset}";
29 | echo "📖 ${blue}Meteor for Windows is available at: ${purple}https://install.meteor.com/windows";
30 | exit;
31 | fi
32 |
33 | }
34 |
35 |
36 | test -f settings.json || (echo "🛠 ${blue}Creating your own settings.json file...\n"; cp sample_settings.json settings.json;)
37 |
38 | echo "🌋 ${bold}${purple}Happy hacking with Vulcan!${reset}";
39 |
40 | echo "📖 ${blue}The docs are available at: ${purple}http://docs.vulcanjs.org";
41 |
42 | if tput setaf 1 &> /dev/null; then
43 | tput sgr0;
44 | fi
45 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/rooms/RoomsMain.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Main column of the RoomsPage component
4 |
5 | */
6 |
7 | import React from 'react';
8 | import { FormattedMessage } from 'meteor/vulcan:i18n';
9 | import { Components, registerComponent } from 'meteor/vulcan:core';
10 |
11 | import Rooms from '../../modules/rooms/collection';
12 |
13 | // import RoomsReviews from './RoomsReviews';
14 |
15 | const RoomsMain = ({ room, currentUser }) =>
16 |
17 |
18 |
19 |
20 |
21 |
{room.name}
22 |
{room.city}
23 |
24 |
{room.description}
25 |
26 | {room.amenities ?
{room.amenities.map(amenity => {amenity} )} : null}
27 |
28 |
29 |
30 |
31 |
32 |
33 | {room.location ? : null}
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | registerComponent('RoomsMain', RoomsMain);
43 |
44 | // export default RoomsMain;
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "eslint:recommended",
4 | "plugin:meteor/recommended",
5 | "plugin:react/recommended"
6 | ],
7 | "parser": "babel-eslint",
8 | "parserOptions": {
9 | "allowImportExportEverywhere": true,
10 | "ecmaVersion": 6,
11 | "sourceType": "module"
12 | },
13 | "rules": {
14 | "babel/generator-star-spacing": 0,
15 | "babel/new-cap": [1, {
16 | "capIsNewExceptions": [
17 | "Optional",
18 | "OneOf",
19 | "Maybe",
20 | "MailChimpAPI",
21 | "Juice",
22 | "Run",
23 | "AppComposer",
24 | "Query",
25 | ]
26 | }],
27 | "babel/array-bracket-spacing": 0,
28 | "babel/object-curly-spacing": 0,
29 | "babel/object-shorthand": 0,
30 | "babel/arrow-parens": 0,
31 | "babel/no-await-in-loop": 1,
32 | "comma-dangle": 0,
33 | "key-spacing": 0,
34 | "no-extra-boolean-cast": 0,
35 | "no-undef": 1,
36 | "no-unused-vars": [1, {
37 | "vars": "all",
38 | "args": "none",
39 | "varsIgnorePattern": "React|PropTypes|Component"
40 | }],
41 | "no-console": 1,
42 | "react/prop-types": 0,
43 | "meteor/audit-argument-checks": 0,
44 | "no-case-declarations": 0
45 | },
46 | "env": {
47 | "browser": true,
48 | "commonjs": true,
49 | "es6": true,
50 | "meteor": true,
51 | "node": true
52 | },
53 | "plugins": [
54 | "babel",
55 | "meteor",
56 | "react"
57 | ],
58 | "settings": {
59 | "import/resolver": "meteor"
60 | },
61 | "modules": true,
62 | "root": true,
63 | "globals": {
64 | "param": true,
65 | "returns": true
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/bookings/BookingsCompleted.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Single booking page, wrapped with withDocument
4 |
5 | http://docs.vulcanjs.org/data-loading.html#Single-Resolver
6 |
7 | */
8 |
9 | import React from 'react';
10 | import { Components, registerComponent, withCurrentUser, withDocument } from 'meteor/vulcan:core';
11 | import mapProps from 'recompose/mapProps';
12 | import compose from 'recompose/compose';
13 | import Button from 'react-bootstrap/lib/Button';
14 | import gql from 'graphql-tag';
15 | import { FormattedMessage } from 'meteor/vulcan:i18n';
16 |
17 | import Bookings from '../../modules/bookings/collection';
18 |
19 | const BookingsCompleted = ({document, loading, currentUser}) =>
20 |
21 |
22 | {loading? 'Loading…' :
23 |
24 |
25 |
26 |
Booking for {document.room.name} completed
27 |
28 |
29 |
30 |
31 |
32 | }
33 |
34 |
35 |
36 | BookingsCompleted.displayName = 'BookingsPage';
37 |
38 | const options = {
39 | collection: Bookings,
40 | fragmentName: 'BookingsItemFragment'
41 | };
42 |
43 | const mapPropsFunction = props => ({...props, documentId: props.routeParams && props.routeParams.bookingId});
44 |
45 | registerComponent('BookingsCompleted', BookingsCompleted, mapProps(mapPropsFunction), [withDocument, options], withCurrentUser);
46 |
47 | // export default compose(
48 | // mapProps(mapPropsFunction),
49 | // withDocument(options),
50 | // withCurrentUser
51 | // )(BookingsCompleted);
52 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/emails.js:
--------------------------------------------------------------------------------
1 | import VulcanEmail from 'meteor/vulcan:email';
2 |
3 | VulcanEmail.addEmails({
4 |
5 | test: {
6 | template: "test",
7 | path: "/email/test",
8 | data() {
9 | return {date: new Date()};
10 | },
11 | subject() {
12 | return "This is a test";
13 | },
14 | },
15 |
16 | roomsNew: {
17 | template: 'roomsNew',
18 | path: '/email/roomsNew',
19 | subject(data) {
20 | const room = _.isEmpty(data) ? {name: '[name]'} : data.RoomsSingle;
21 | return `A new room has been created: ${room.name}`;
22 | },
23 | query: `
24 | query OneRoom($documentId: String){
25 | RoomsSingle(documentId: $documentId){
26 | name
27 | description
28 | user{
29 | _id
30 | displayName
31 | }
32 | }
33 | }
34 | `,
35 | testVariables: {}
36 | },
37 |
38 | bookingsNew: {
39 | template: 'bookingsNew',
40 | path: '/email/bookingsNew',
41 | subject(data) {
42 | const booking = _.isEmpty(data) ? {room: {name: '[name]'}} : data.BookingsSingle;
43 | return `A booking has been created for room: ${booking.room.name}`;
44 | },
45 | query: `
46 | query OneBooking($documentId: String){
47 | BookingsSingle(documentId: $documentId){
48 | _id
49 | pageUrl
50 | startAtFormatted
51 | endAtFormatted
52 | numberOfGuests
53 | user{
54 | _id
55 | displayName
56 | pageUrl
57 | }
58 | room{
59 | _id
60 | name
61 | description
62 | pageUrl
63 | }
64 | }
65 | }
66 | `,
67 | testVariables: {}
68 | }
69 |
70 | });
71 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/rooms/RoomsPhotos.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Display a room's photo
4 |
5 | */
6 |
7 | import React, { PureComponent } from 'react';
8 | import { Components, registerComponent, withCurrentUser, withDocument } from 'meteor/vulcan:core';
9 | import mapProps from 'recompose/mapProps';
10 | import compose from 'recompose/compose';
11 | import { FormattedMessage } from 'meteor/vulcan:i18n';
12 |
13 | import Rooms from '../../modules/rooms/collection';
14 |
15 | class RoomsPhotos extends PureComponent {
16 |
17 | constructor() {
18 | super();
19 | this.previous = this.previous.bind(this);
20 | this.next = this.next.bind(this);
21 | this.state = {
22 | selected: 0
23 | };
24 | }
25 |
26 | previous(e) {
27 | e.preventDefault();
28 | const totalPhotos = this.props.room.photos.length;
29 | this.setState({
30 | selected: this.state.selected === 0 ? totalPhotos - 1 : this.state.selected - 1
31 | });
32 | }
33 |
34 | next(e) {
35 | e.preventDefault();
36 | const totalPhotos = this.props.room.photos.length;
37 | this.setState({
38 | selected: this.state.selected === totalPhotos - 1 ? 0 : this.state.selected + 1
39 | });
40 | }
41 |
42 | render() {
43 |
44 | return(
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | )
53 | }
54 | }
55 |
56 | RoomsPhotos.displayName = 'RoomsPhotos';
57 |
58 | registerComponent('RoomsPhotos', RoomsPhotos);
59 |
60 | // export default RoomsPhotos;
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/rooms/parameters.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Rooms parameters
4 |
5 | http://docs.vulcanjs.org/terms-parameters.html
6 |
7 | */
8 |
9 | import { addCallback } from 'meteor/vulcan:core';
10 | import moment from 'moment';
11 |
12 | function addFromToParameters (parameters, terms, apolloClient, context) {
13 |
14 | if (terms.from || terms.to) {
15 |
16 | const mFrom = moment(terms.from, "YYYY-MM-DD").startOf('day');
17 | const mTo = moment(terms.to, "YYYY-MM-DD").endOf('day');
18 |
19 | /*
20 | Find all bookings during that period that:
21 | - End between "from" and "to"
22 | - Start between "from" and "to"
23 | */
24 | const currentBookings = context.Bookings.find({$or: [
25 | {$and: [ {startAt: {"$gt": mFrom.toDate()}}, {startAt: {"$lt": mTo.toDate()}} ]},
26 | {$and: [ {endAt: {"$gt": mFrom.toDate()}}, {endAt: {"$lt": mTo.toDate()}} ]},
27 | ]}).fetch();
28 | const bookingsRoomIds = _.unique(_.pluck(currentBookings, 'roomId'));
29 |
30 | parameters.selector._id = {$nin: bookingsRoomIds};
31 | }
32 |
33 | return parameters;
34 | }
35 | addCallback('rooms.parameters', addFromToParameters);
36 |
37 | function addFiltersParameter (parameters, terms, apolloClient) {
38 |
39 | if (terms.filters) {
40 | const filters = Array.isArray(terms.filters) ? terms.filters : [terms.filters];
41 | parameters.selector.amenities = { $all: filters };
42 | }
43 |
44 | return parameters;
45 | }
46 | addCallback('rooms.parameters', addFiltersParameter);
47 |
48 | function addSwNeParameters (parameters, terms, apolloClient) {
49 |
50 | if (terms.sw && terms.ne) {
51 | parameters.selector.location = {
52 | $geoWithin: {
53 | $box: [
54 | [terms.sw.lng, terms.sw.lat],
55 | [terms.ne.lng, terms.ne.lat]
56 | ]
57 | }
58 | }
59 | }
60 |
61 | return parameters;
62 | }
63 | addCallback('rooms.parameters', addSwNeParameters);
64 |
--------------------------------------------------------------------------------
/.meteor/versions:
--------------------------------------------------------------------------------
1 | accounts-base@1.3.4
2 | accounts-password@1.4.0
3 | allow-deny@1.0.9
4 | autoupdate@1.3.12
5 | babel-compiler@6.20.0
6 | babel-runtime@1.0.1
7 | base64@1.0.10
8 | binary-heap@1.0.10
9 | boilerplate-generator@1.2.0
10 | buffer@0.0.0
11 | caching-compiler@1.1.9
12 | callback-hook@1.0.10
13 | check@1.2.5
14 | ddp@1.3.1
15 | ddp-client@2.1.3
16 | ddp-common@1.2.9
17 | ddp-rate-limiter@1.0.7
18 | ddp-server@2.0.2
19 | diff-sequence@1.0.7
20 | dynamic-import@0.1.3
21 | ecmascript@0.8.3
22 | ecmascript-runtime@0.4.1
23 | ecmascript-runtime-client@0.4.3
24 | ecmascript-runtime-server@0.4.1
25 | ejson@1.0.14
26 | email@1.2.3
27 | fourseven:scss@4.5.4
28 | geojson-utils@1.0.10
29 | hot-code-push@1.0.4
30 | http@1.2.12
31 | id-map@1.0.9
32 | livedata@1.0.18
33 | localstorage@1.1.1
34 | logging@1.1.17
35 | meteor@1.7.2
36 | meteor-base@1.1.0
37 | meteorhacks:inject-initial@1.0.4
38 | meteorhacks:picker@1.0.3
39 | minifier-css@1.2.16
40 | minifier-js@2.1.4
41 | minimongo@1.3.2
42 | modules@0.10.0
43 | modules-runtime@0.8.0
44 | mongo@1.2.2
45 | mongo-dev-server@1.0.1
46 | mongo-id@1.0.6
47 | npm-bcrypt@0.9.3
48 | npm-mongo@2.2.30
49 | ordered-dict@1.0.9
50 | percolatestudio:synced-cron@1.1.0
51 | promise@0.9.0
52 | random@1.0.10
53 | rate-limit@1.0.8
54 | reactive-dict@1.1.9
55 | reactive-var@1.0.11
56 | reload@1.1.11
57 | retry@1.0.9
58 | routepolicy@1.0.12
59 | service-configuration@1.0.11
60 | session@1.1.7
61 | sha@1.0.9
62 | shell-server@0.2.4
63 | srp@1.0.10
64 | standard-minifier-css@1.3.5
65 | standard-minifier-js@2.1.2
66 | standard-minifiers@1.1.0
67 | tracker@1.1.3
68 | underscore@1.0.10
69 | url@1.1.0
70 | vulcan:accounts@1.8.0
71 | vulcan:admin@1.8.0
72 | vulcan:core@1.8.0
73 | vulcan:debug@1.8.0
74 | vulcan:email@1.8.0
75 | vulcan:forms@1.8.0
76 | vulcan:forms-upload@1.8.0
77 | vulcan:i18n@1.8.0
78 | vulcan:i18n-en-us@1.8.0
79 | vulcan:lib@1.8.0
80 | vulcan:maps@1.6.0
81 | vulcan:payments@1.8.0
82 | vulcan:routing@1.8.0
83 | vulcan:users@1.8.0
84 | webapp@1.3.19
85 | webapp-hashing@1.0.9
86 | zensroom@0.0.0
87 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/admin/BookingsDashboard.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Show a list of all bookings
4 |
5 | http://docs.vulcanjs.org/core-components.html#Datatable
6 |
7 | */
8 |
9 | import React from 'react';
10 | import { Components, registerComponent } from 'meteor/vulcan:core';
11 | import compose from 'recompose/compose';
12 | import { FormattedMessage } from 'meteor/vulcan:i18n';
13 | import { Link } from 'react-router';
14 |
15 | import Bookings from '../../modules/bookings/collection.js';
16 |
17 | const BookingPrice = ({ document }) => {document.room.pricePerNight}
;
18 |
19 | const BookingUser = ({ document }) =>
20 |
21 |
22 |
23 | {document.user.displayName}
24 |
25 |
;
26 |
27 | const BookingRoom = ({ document }) =>
28 |
29 |
30 |
31 |
{document.room.name}
32 |
33 |
;
34 |
35 | const BookingsDashboard = () =>
36 |
37 |
38 |
39 |
40 |
41 |
64 |
65 |
66 |
67 | registerComponent('BookingsDashboard', BookingsDashboard);
68 |
69 | // export default BookingsDashboard;
70 |
--------------------------------------------------------------------------------
/sample_settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "public": {
3 | "title": "Your site title",
4 | "tagline":"Your site tagline",
5 |
6 | "logoUrl": "http://placekitten.com/250/80",
7 | "logoHeight": "80",
8 | "logoWidth": "250",
9 | "faviconUrl": "/favicon.ico",
10 |
11 | "requirePostsApproval":false,
12 | "requireViewInvite":false,
13 | "requirePostInvite":false,
14 | "enableNewsletter":true,
15 | "autoSubscribe":false,
16 | "emailNotifications":true,
17 | "postInterval": 20,
18 | "RSSLinksPointTo": "link",
19 | "commentInterval": 20,
20 | "maxPostsPerDay": 10,
21 | "startInvitesCount": 5,
22 | "postsPerPage": 10,
23 |
24 | "language": "en",
25 | "locale": "en",
26 |
27 | "twitterAccount": "foo",
28 | "facebookPage": "http://facebook.com/foo",
29 |
30 | "googleAnalyticsId":"123foo",
31 | "useSegment": false,
32 | "segmentWriteKey": "456bar"
33 | },
34 |
35 | "defaultEmail": "hello@world.com",
36 | "mailUrl": "smtp://username%40yourdomain.mailgun.org:yourpassword123@smtp.mailgun.org:587/",
37 | "scoreUpdateInterval": "30",
38 |
39 | "embedlyKey":"123foo",
40 |
41 | "newsletterProvider": "mailchimp",
42 | "mailchimp": {
43 | "apiKey": "123foo",
44 | "listId": "123foo"
45 | },
46 | "newsletterFrequency": [1,2,3,4,5,6,7],
47 | "newsletterTime": "14:20",
48 | "newsletterExcerptLength": 20,
49 | "postExcerptLength": 30,
50 |
51 | "categories": [
52 | {
53 | "name": "Test Category 1",
54 | "description": "The first test category",
55 | "order": 4,
56 | "slug": "testcat1"
57 | },
58 | {
59 | "name": "Test Category 2",
60 | "description": "The second test category",
61 | "order": 7,
62 | "slug": "testcat2"
63 | },
64 | {
65 | "name": "Test Category 3",
66 | "description": "The third test category",
67 | "order": 10,
68 | "slug": "testcat3"
69 | }
70 | ],
71 | "oAuth": {
72 | "twitter": {
73 | "consumerKey": "foo",
74 | "secret": "bar"
75 | },
76 | "facebook": {
77 | "appId": "foo",
78 | "secret": "bar"
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/i18n.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Internationalization Strings
4 |
5 | http://docs.vulcanjs.org/internationalization.html
6 |
7 | */
8 |
9 | import { addStrings } from 'meteor/vulcan:core';
10 |
11 | addStrings('en', {
12 | 'nav.how_to': 'How to Book',
13 | 'nav.about': 'About',
14 |
15 | 'bookings.bookings': 'Bookings',
16 | 'bookings.please_fill_in_all_fields': 'Please fill in all fields.',
17 | 'bookings.created': 'Booking created',
18 | 'bookings.book': 'Book This Room',
19 | 'bookings.paid_on': 'Paid on',
20 | 'bookings.complete_payment': 'Complete Payment',
21 | 'bookings.bookings_admin': 'Bookings',
22 | 'bookings.your_bookings': 'Your bookings for this room:',
23 | 'bookings.no_bookings': 'No bookings for this room yet.',
24 | 'bookings.load_more': 'Load More',
25 | 'bookings.from': 'From',
26 | 'bookings.to': 'To',
27 | 'bookings.number_of_guests': 'Number of guests',
28 | 'bookings.past': 'Past Bookings',
29 | 'bookings.current': 'Current Bookings',
30 | 'bookings.future': 'Future Bookings',
31 | 'bookings.dates': 'Dates',
32 |
33 | 'rooms.rooms': 'Rooms',
34 | 'rooms.rooms_admin': 'Rooms',
35 | 'rooms.new': 'New Room',
36 | 'rooms.create_new': 'Create New Room',
37 | 'rooms.created': 'New room created',
38 | 'rooms.featured': 'Featured Rooms',
39 | 'rooms.load_more': 'Load More',
40 | 'rooms.search_results': 'Search Results',
41 | 'rooms.location': 'Location',
42 | 'rooms.to': 'To',
43 | 'rooms.from': 'From',
44 | 'rooms.search': 'Search',
45 | 'rooms.per_night': '/night',
46 | 'rooms.filters': 'Filters',
47 | 'rooms.with_fireplace': 'With Fireplace',
48 |
49 | 'reviews.reviews': 'Reviews',
50 | 'reviews.reviews_admin': 'Reviews',
51 | 'reviews.created': 'Review created',
52 | 'reviews.load_more': 'Load More',
53 | 'reviews.leave_review': 'Leave a Review',
54 |
55 | 'users.rooms': 'Rooms',
56 | 'users.bookings': 'Bookings',
57 | 'users.reviews': 'Reviews',
58 |
59 | 'accounts.already_have_an_account': 'Already have an account?',
60 | 'accounts.log_in_here': 'Log in here',
61 | 'accounts.dont_have_an_account': 'Don\'t have an account?',
62 | 'accounts.sign_up_here': 'Sign up here',
63 |
64 | });
65 |
--------------------------------------------------------------------------------
/packages/_boilerplate-generator/template-web.browser.js:
--------------------------------------------------------------------------------
1 | // Template function for rendering the boilerplate html for browsers
2 |
3 | export default function({
4 | meteorRuntimeConfig,
5 | rootUrlPathPrefix,
6 | inlineScriptsAllowed,
7 | css,
8 | js,
9 | additionalStaticJs,
10 | htmlAttributes,
11 | bundledJsCssUrlRewriteHook,
12 | head,
13 | body,
14 | dynamicHead,
15 | dynamicBody,
16 | }) {
17 | return [].concat(
18 | [
19 | '
20 | _.template(' <%= attrName %>="<%- attrValue %>"')({
21 | attrName: key,
22 | attrValue: value
23 | })
24 | ).join('') + '>',
25 | ''
26 | ],
27 |
28 | [
29 | head,
30 | dynamicHead,
31 | ],
32 |
33 | _.map(css, ({url}) =>
34 | _.template(' ')({
35 | href: bundledJsCssUrlRewriteHook(url)
36 | })
37 | ),
38 |
39 | [
40 | '',
41 | '',
42 | body,
43 | dynamicBody,
44 | '',
45 | (inlineScriptsAllowed
46 | ? _.template(' ')({
47 | conf: meteorRuntimeConfig
48 | })
49 | : _.template(' ')({
50 | src: rootUrlPathPrefix
51 | })
52 | ) ,
53 | ''
54 | ],
55 |
56 | _.map(js, ({url}) =>
57 | _.template(' ')({
58 | src: bundledJsCssUrlRewriteHook(url)
59 | })
60 | ),
61 |
62 | _.map(additionalStaticJs, ({contents, pathname}) => (
63 | (inlineScriptsAllowed
64 | ? _.template(' ')({
65 | contents: contents
66 | })
67 | : _.template(' ')({
68 | src: rootUrlPathPrefix + pathname
69 | }))
70 | )),
71 |
72 | [
73 | '', '',
74 | '',
75 | ''
76 | ],
77 | ).join('\n');
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/components.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Components
4 |
5 | http://docs.vulcanjs.org/theming.html
6 |
7 | */
8 |
9 | import '../components/admin/AdminUsersBookings';
10 | import '../components/admin/AdminUsersReviews';
11 | import '../components/admin/AdminUsersRooms';
12 | import '../components/admin/BookingsDashboard';
13 | import '../components/admin/RoomsDashboard';
14 | import '../components/admin/ReviewsDashboard';
15 |
16 | import '../components/bookings/BookingsNewForm';
17 | import '../components/bookings/BookingsPage';
18 | import '../components/bookings/BookingsRoomUser';
19 | import '../components/bookings/BookingsPending';
20 | import '../components/bookings/BookingsPast';
21 | import '../components/bookings/BookingsCurrent';
22 | import '../components/bookings/BookingsFuture';
23 | import '../components/bookings/BookingsCompleted';
24 |
25 | import '../components/common/Footer';
26 | import '../components/common/Header';
27 | import '../components/common/Home';
28 | import '../components/common/Layout';
29 | import '../components/common/AdminLayout';
30 |
31 | import '../components/reviews/ReviewsItem';
32 | import '../components/reviews/ReviewsList';
33 | import '../components/reviews/ReviewsNewForm';
34 |
35 | import '../components/rooms/RoomsItem';
36 | import '../components/rooms/RoomsList';
37 | import '../components/rooms/RoomsMain';
38 | import '../components/rooms/RoomsNewForm';
39 | import '../components/rooms/RoomsNewPage';
40 | import '../components/rooms/RoomsPage';
41 | import '../components/rooms/RoomsPhotos';
42 | import '../components/rooms/RoomsReviews';
43 | import '../components/rooms/RoomsSearch';
44 | import '../components/rooms/RoomsSearchFilters';
45 | import '../components/rooms/RoomsSearchForm';
46 | import '../components/rooms/RoomsSearchResults';
47 |
48 | import '../components/static/About';
49 | import '../components/static/HowTo';
50 | import '../components/static/Privacy';
51 | import '../components/static/Terms';
52 |
53 | import '../components/users/UsersAccount';
54 | import '../components/users/UsersAccountMenu';
55 | import '../components/users/UsersMenu';
56 | import '../components/users/UsersProfile';
57 | import '../components/users/UsersSignUp';
58 | import '../components/users/UsersLogIn';
59 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/users/UsersProfile.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | User profile page
4 |
5 | */
6 |
7 | import { Components, registerComponent, withDocument, withCurrentUser } from 'meteor/vulcan:core';
8 | import React from 'react';
9 | import { FormattedMessage } from 'meteor/vulcan:i18n';
10 | import Users from 'meteor/vulcan:users';
11 | import { Link } from 'react-router';
12 | import mapProps from 'recompose/mapProps';
13 | import compose from 'recompose/compose';
14 |
15 | const UsersProfile = (props) => {
16 | if (props.loading) {
17 |
18 | return
19 |
20 | } else if (!props.document) {
21 |
22 | console.log(`// missing user (_id/slug: ${props.documentId || props.slug})`);
23 | return
24 |
25 | } else {
26 |
27 | const user = props.document;
28 |
29 | const terms = {view: "userPosts", userId: user._id};
30 |
31 | return (
32 |
33 |
34 |
{Users.getDisplayName(user)}
35 |
40 |
41 | )
42 | }
43 | }
44 |
45 | UsersProfile.propTypes = {
46 | // document: PropTypes.object.isRequired,
47 | }
48 |
49 | UsersProfile.displayName = "UsersProfile";
50 |
51 | const options = {
52 | collection: Users,
53 | queryName: 'usersSingleQuery',
54 | // fragmentName: 'UsersProfile',
55 | };
56 |
57 | const mapPropsFunction = props => ({...props, userId: props.routeParams && props.routeParams.userId, slug: props.routeParams && props.routeParams.slug});
58 |
59 | registerComponent('UsersProfile', UsersProfile, mapProps(mapPropsFunction), withCurrentUser, [withDocument, options]);
60 |
61 | // export default compose(
62 | // mapProps(mapPropsFunction),
63 | // withCurrentUser,
64 | // withDocument(options),
65 | // )(UsersProfile);
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/rooms/RoomsPage.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | A single room's page. Wrapped with withDocument.
4 |
5 | http://docs.vulcanjs.org/data-loading.html#Single-Resolver
6 |
7 | */
8 |
9 | import React from 'react';
10 | import { Components, registerComponent, withCurrentUser, withDocument } from 'meteor/vulcan:core';
11 | import mapProps from 'recompose/mapProps';
12 | import compose from 'recompose/compose';
13 | import Button from 'react-bootstrap/lib/Button';
14 | import { FormattedMessage } from 'meteor/vulcan:i18n';
15 |
16 | import Rooms from '../../modules/rooms/collection';
17 |
18 | // import RoomsPhotos from './RoomsPhotos';
19 | // import RoomsMain from './RoomsMain';
20 |
21 | // import BookingsNewForm from '../bookings/BookingsNewForm';
22 | // import BookingsRoomUser from '../bookings/BookingsRoomUser';
23 |
24 | const RoomsPage = ({document: room, documentId, loading, currentUser}) =>
25 |
26 |
27 |
28 | {loading?
29 |
30 |
:
31 |
32 |
33 |
34 | {room.photos && room.photos.length ?
35 |
}>
36 |
37 |
38 | : null}
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | {/*currentUser ?
49 |
50 | : null*/}
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | }
59 |
60 |
61 |
62 |
63 | RoomsPage.displayName = 'RoomsPage';
64 |
65 | const options = {
66 | collection: Rooms
67 | };
68 |
69 | const mapPropsFunction = props => ({...props, documentId: props.routeParams && props.routeParams.roomId});
70 |
71 | registerComponent('RoomsPage', RoomsPage, mapProps(mapPropsFunction), [withDocument, options], withCurrentUser);
72 |
73 | // export default compose(
74 | // mapProps(mapPropsFunction),
75 | // withDocument(options),
76 | // withCurrentUser
77 | // )(RoomsPage);
78 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/users/UsersAccount.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Edit user account page
4 |
5 | */
6 |
7 | import { Components, registerComponent, withCurrentUser, withMessages } from 'meteor/vulcan:core';
8 | import React from 'react';
9 | import PropTypes from 'prop-types';
10 | import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n';
11 | import Users from 'meteor/vulcan:users';
12 | import { STATES } from 'meteor/vulcan:accounts';
13 | import mapProps from 'recompose/mapProps';
14 | import compose from 'recompose/compose';
15 |
16 | const UsersAccount = (props, context) => {
17 | return (
18 | }
22 | >
23 |
24 |
25 |
26 |
27 | }>
28 |
29 |
30 |
31 |
32 |
{
36 | props.flash(context.intl.formatMessage({ id: 'users.edit_success' }, {name: Users.getDisplayName(user)}), 'success')
37 | }}
38 | showRemove={true}
39 | />
40 |
41 |
42 | );
43 | };
44 |
45 |
46 | UsersAccount.propTypes = {
47 | terms: PropTypes.object, // a user is defined by its unique _id or its unique slug
48 | };
49 |
50 | UsersAccount.contextTypes = {
51 | intl: intlShape
52 | };
53 |
54 | UsersAccount.displayName = 'UsersAccount';
55 |
56 | const mapPropsFunction = props => ({...props, terms: props.routeParams && props.routeParams.slug ? {slug: props.routeParams.slug} : {documentId: props.currentUser._id}});
57 |
58 | registerComponent('UsersAccount', UsersAccount, mapProps(mapPropsFunction), withMessages, withCurrentUser);
59 |
60 | // export default compose(
61 | // mapProps(mapPropsFunction),
62 | // withMessages,
63 | // withCurrentUser,
64 | // )(UsersAccount);
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/bookings/BookingsPage.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Single booking page, wrapped with withDocument
4 |
5 | http://docs.vulcanjs.org/data-loading.html#Single-Resolver
6 |
7 | */
8 |
9 | import React from 'react';
10 | import { Components, registerComponent, withCurrentUser, withDocument } from 'meteor/vulcan:core';
11 | import mapProps from 'recompose/mapProps';
12 | import compose from 'recompose/compose';
13 | import Button from 'react-bootstrap/lib/Button';
14 | import gql from 'graphql-tag';
15 | import { FormattedMessage } from 'meteor/vulcan:i18n';
16 | import { withRouter } from 'react-router';
17 | import Bookings from '../../modules/bookings/collection';
18 |
19 | const BookingsPage = ({document, loading, currentUser, router}) =>
20 |
21 |
22 | {loading? 'Loading…' :
23 |
24 |
25 |
26 |
{document.room.name}
27 |
28 |
29 | {document.paidAt?
30 |
{document.paidAt}
:
31 |
{
42 | router.push(`/booking/${document._id}/completed`)
43 | }}
44 | button={ }
45 | />
46 | }
47 |
48 |
49 |
50 |
51 |
52 |
53 | }
54 |
55 |
56 |
57 | BookingsPage.displayName = 'BookingsPage';
58 |
59 | const options = {
60 | collection: Bookings,
61 | fragmentName: 'BookingsItemFragment'
62 | };
63 |
64 | const mapPropsFunction = props => ({...props, documentId: props.routeParams && props.routeParams.bookingId});
65 |
66 | registerComponent('BookingsPage', BookingsPage, mapProps(mapPropsFunction), [withDocument, options], withCurrentUser, withRouter);
67 |
68 | // export default compose(
69 | // mapProps(mapPropsFunction),
70 | // withDocument(options),
71 | // withCurrentUser,
72 | // withRouter,
73 | // )(BookingsPage);
74 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/custom_fields.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Custom fields
4 |
5 | http://docs.vulcanjs.org/schemas.html#Custom-Fields
6 |
7 | */
8 |
9 | import Users from 'meteor/vulcan:users';
10 |
11 | Users.addField([
12 | {
13 | fieldName: 'rooms',
14 | fieldSchema: {
15 | type: Array,
16 | optional: true,
17 | viewableBy: ['guests'],
18 | resolveAs: {
19 | fieldName: 'rooms',
20 | arguments: 'limit: Int = 5',
21 | type: '[Room]',
22 | resolver: (user, { limit }, { currentUser, Users, Rooms }) => {
23 | const rooms = Rooms.find({ userId: user._id }, { limit }).fetch();
24 |
25 | // restrict documents fields
26 | // const viewableRooms = _.filter(rooms, room => Rooms.checkAccess(currentUser, room));
27 | const restrictedRooms = Users.restrictViewableFields(currentUser, Rooms, rooms);
28 |
29 | return restrictedRooms;
30 | }
31 | }
32 | }
33 | },
34 | {
35 | fieldName: 'bookings',
36 | fieldSchema: {
37 | type: Array,
38 | optional: true,
39 | viewableBy: ['guests'],
40 | resolveAs: {
41 | fieldName: 'bookings',
42 | arguments: 'limit: Int = 5',
43 | type: '[Booking]',
44 | resolver: (user, { limit }, { currentUser, Users, Bookings }) => {
45 | const bookings = Bookings.find({ userId: user._id }, { limit }).fetch();
46 |
47 | // restrict documents fields
48 | // const viewableRooms = _.filter(rooms, room => Rooms.checkAccess(currentUser, room));
49 | const restrictedBookings = Users.restrictViewableFields(currentUser, Bookings, bookings);
50 |
51 | return restrictedBookings;
52 | }
53 | }
54 | }
55 | },
56 | {
57 | fieldName: 'reviews',
58 | fieldSchema: {
59 | type: Array,
60 | optional: true,
61 | viewableBy: ['guests'],
62 | resolveAs: {
63 | fieldName: 'reviews',
64 | arguments: 'limit: Int = 5',
65 | type: '[Review]',
66 | resolver: (user, { limit }, { currentUser, Users, Reviews }) => {
67 | const reviews = Reviews.find({ userId: user._id }, { limit }).fetch();
68 |
69 | // restrict documents fields
70 | // const viewableRooms = _.filter(rooms, room => Rooms.checkAccess(currentUser, room));
71 | const restrictedReviews = Users.restrictViewableFields(currentUser, Reviews, reviews);
72 |
73 | return restrictedReviews;
74 | }
75 | }
76 | }
77 | },
78 | ]);
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/rooms/RoomsSearchResults.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Room search results. Wrapped with withList.
4 |
5 | http://docs.vulcanjs.org/data-loading.html#List-Resolver
6 |
7 | */
8 |
9 | import React from 'react';
10 | import { Components, registerComponent, withList } from 'meteor/vulcan:core';
11 | import { FormattedMessage } from 'meteor/vulcan:i18n';
12 |
13 | import Rooms from '../../modules/rooms/collection';
14 | // import RoomsItem from './RoomsItem';
15 |
16 | const defaultMapProperties = {lat: 35.6895, lng: 139.6917, type: 'country'}; // Tokyo, Japan
17 |
18 | const getCoords = room => room.location && {lng: room.location.coordinates[0], lat: room.location.coordinates[1]};
19 |
20 | const getZoom = type => {
21 | switch(type) {
22 | case 'country':
23 | return 4;
24 | case 'locality':
25 | return 10;
26 | case 'street_address':
27 | return 15;
28 | }
29 | }
30 |
31 | const RoomsSearchResults = ({results = [], currentUser, loading, loadMore, count, totalCount, terms, onMapChange, mapProperties}) =>
32 |
33 |
34 |
35 |
36 |
37 | {loading ?
38 |
39 |
:
40 |
41 |
42 |
43 |
51 |
52 |
59 |
60 |
61 | }
62 |
63 |
64 |
65 |
66 | // const mapPropsFunction = props => ({...props, terms: {...props.location.query}});
67 |
68 | const options = {
69 | collection: Rooms,
70 | enableReducer: false
71 | }
72 |
73 | registerComponent('RoomsSearchResults', RoomsSearchResults, [withList, options]);
74 |
75 | // export default withList(options)(RoomsSearchResults);
--------------------------------------------------------------------------------
/packages/zensroom/lib/server/rooms/callbacks.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Server callbacks
4 |
5 | See: http://docs.vulcanjs.org/callbacks.html
6 |
7 | */
8 |
9 | import { addCallback } from 'meteor/vulcan:core';
10 | import { geocode } from 'meteor/vulcan:maps';
11 | import Users from 'meteor/vulcan:users';
12 | import VulcanEmail from 'meteor/vulcan:email';
13 |
14 | const geoFields = ['address', 'address2', 'state', 'city', 'zipCode', 'country'];
15 |
16 | const getAddressString = document => _.compact(geoFields.map(field => document[field])).join(' ');
17 |
18 | /*
19 |
20 | When a new room is created, geocode its address
21 |
22 | */
23 | async function geocodeAddressOnNewRoom (document, currentUser) {
24 | if (_.some(geoFields, field => !!document[field])) {
25 | try {
26 | const geoData = await geocode(getAddressString(document));
27 | document = {
28 | ...document,
29 | geoData,
30 | location: {
31 | type: 'Point',
32 | coordinates: [geoData.geometry.location.lng, geoData.geometry.location.lat]
33 | }
34 | }
35 | } catch (error) {
36 | console.log('//geoData error')
37 | console.log(error)
38 | }
39 | }
40 | return document;
41 | }
42 | addCallback('rooms.new.sync', geocodeAddressOnNewRoom);
43 |
44 | /*
45 |
46 | When a room is edited, geocode its address
47 |
48 | */
49 | async function geocodeAddressOnEditRoom (modifier, document, currentUser) {
50 | if (_.some(geoFields, field => modifier.$set[field] && modifier.$set[field] !== document[field])) {
51 | try {
52 | const geoData = await geocode(getAddressString(modifier.$set));
53 | modifier.$set = {
54 | ...modifier.$set,
55 | geoData,
56 | location: {
57 | type: 'Point',
58 | coordinates: [geoData.geometry.location.lng, geoData.geometry.location.lat]
59 | }
60 | }
61 | } catch (error) {
62 | console.log('//geoData error')
63 | console.log(error)
64 | }
65 | }
66 | return modifier;
67 | }
68 | addCallback('rooms.edit.sync', geocodeAddressOnEditRoom);
69 |
70 | /*
71 |
72 | When a new room is created, send a notification to all admins
73 |
74 | */
75 | async function RoomsNewNotifications (room) {
76 |
77 | const adminUsers = Users.find({isAdmin : true, _id: {$ne: room.userId}}).fetch();
78 | const emails = _.compact(_.pluck(adminUsers, 'email'));
79 |
80 | await VulcanEmail.buildAndSend({
81 | to: emails,
82 | emailName: 'roomsNew',
83 | variables: { documentId: room._id}
84 | });
85 |
86 | }
87 | addCallback('rooms.new.async', RoomsNewNotifications);
88 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/rooms/RoomsSearch.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Room search page. Contains form, filters, and results
4 |
5 | */
6 |
7 | import React, { Component } from 'react';
8 | import { Components, registerComponent } from 'meteor/vulcan:core';
9 | import { withRouter } from 'react-router';
10 |
11 | // import RoomsSearchForm from './RoomsSearchForm';
12 | // import RoomsSearchResults from './RoomsSearchResults';
13 | // import RoomsSearchFilters from './RoomsSearchFilters';
14 |
15 | class RoomsSearch extends Component {
16 |
17 | constructor(props) {
18 | super(props);
19 | this.onMapChange = this.onMapChange.bind(this);
20 |
21 | // initialize state with lat/lng values provided by the URL query parameters
22 | this.state = {
23 | from: props.location.query.from,
24 | to: props.location.query.to,
25 | lat: props.location.query.lat,
26 | lng: props.location.query.lng,
27 | type: props.location.query.type,
28 | };
29 |
30 | }
31 |
32 | // whenever URL change, also update component's state
33 | componentWillReceiveProps(nextProps) {
34 | this.state = {
35 | from: nextProps.location.query.from,
36 | to: nextProps.location.query.to,
37 | lat: nextProps.location.query.lat,
38 | lng: nextProps.location.query.lng,
39 | type: nextProps.location.query.type,
40 | };
41 | }
42 |
43 | onMapChange(mapData) {
44 | // whenever map changes, update this component's state
45 | const { center, zoom, bounds, marginBounds, size } = mapData;
46 | this.setState({
47 | lat: center.lat,
48 | lng: center.lng,
49 | sw: bounds.sw,
50 | ne: bounds.ne,
51 | });
52 | }
53 |
54 | render() {
55 | // mapsProps object to center map on lat/lng and terms object to perform server query
56 | // Note: terms will not have sw/ne until onMapChange triggers for the first time.
57 | return (
58 |
59 |
60 |
61 |
76 |
77 | )
78 | }
79 | }
80 |
81 | registerComponent('RoomsSearch', RoomsSearch, withRouter);
82 |
83 | // export default withRouter(RoomsSearch);
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/rooms/RoomsSearchFilters.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Room search filters
4 |
5 | */
6 |
7 | import React, { Component } from 'react';
8 | import { Components, registerComponent } from 'meteor/vulcan:core';
9 | import { withRouter } from 'react-router';
10 | import { Checkbox } from 'formsy-react-components';
11 | import { amenities, spaces } from '../../modules/data';
12 | import { FormattedMessage } from 'meteor/vulcan:i18n';
13 |
14 | const encodeObject = obj => _.map(obj, (value, key) => `${key}=${value}`).join('&');
15 | const encodeArray = array => _.map(array, (value) => `filters=${value}`).join('&');
16 |
17 | class RoomsSearchFilters extends Component {
18 |
19 | constructor(props) {
20 | super(props);
21 | this.toggleFilter = this.toggleFilter.bind(this);
22 | this.getURLFilters = this.getURLFilters.bind(this);
23 | }
24 |
25 | // get filters from URL
26 | // Note: handle cases where there's 0 filters, 1 filter, or 2+ filters
27 | getURLFilters() {
28 | const filters = this.props.location.query.filters;
29 | return filters ? Array.isArray(filters) ? filters : [filters] : [];
30 | }
31 |
32 | // toggle filter on/off
33 | toggleFilter(filterName) {
34 |
35 | let filters = this.getURLFilters();
36 |
37 | if(_.contains(filters, filterName)) {
38 | // if URL contains filter, remove it
39 | filters = _.without(filters, filterName);
40 | } else {
41 | // if URL doesn't contain filter, add it
42 | filters.push(filterName);
43 | }
44 |
45 | // remove old filters
46 | const query = _.clone(this.props.location.query);
47 | delete query.filters;
48 |
49 | // build URL from current query string and new filters object
50 | let newUrl = `/search?`;
51 | newUrl += encodeObject(query);
52 | if (filters.length) {
53 | newUrl += `&${encodeArray(filters)}`;
54 | }
55 |
56 | // console.log(query)
57 | // console.log(filters)
58 | // console.log(newUrl)
59 |
60 | // replace URL
61 | this.props.router.replace(newUrl);
62 | }
63 |
64 | render() {
65 | const filters = this.getURLFilters();
66 |
67 | return (
68 |
80 | )
81 | }
82 | }
83 |
84 | registerComponent('RoomsSearchFilters', RoomsSearchFilters, withRouter);
85 |
86 | // export default withRouter(RoomsSearchFilters);
--------------------------------------------------------------------------------
/packages/_boilerplate-generator/template-web.cordova.js:
--------------------------------------------------------------------------------
1 | // Template function for rendering the boilerplate html for cordova
2 |
3 | export default function({
4 | meteorRuntimeConfig,
5 | rootUrlPathPrefix,
6 | inlineScriptsAllowed,
7 | css,
8 | js,
9 | additionalStaticJs,
10 | htmlAttributes,
11 | bundledJsCssUrlRewriteHook,
12 | head,
13 | body,
14 | dynamicHead,
15 | dynamicBody,
16 | }) {
17 | return [].concat(
18 | [
19 | '',
20 | '',
21 | ' ',
22 | ' ',
23 | ' ',
24 | ' ',
25 | ' ',
26 | ],
27 | // We are explicitly not using bundledJsCssUrlRewriteHook: in cordova we serve assets up directly from disk, so rewriting the URL does not make sense
28 | _.map(css, ({url}) =>
29 | _.template(' ')({
30 | href: url
31 | })
32 | ),
33 | [
34 | ' ',
48 | '',
49 | ' '
50 | ],
51 | _.map(js, ({url}) =>
52 | _.template(' ')({
53 | src: url
54 | })
55 | ),
56 |
57 | _.map(additionalStaticJs, ({contents, pathname}) => (
58 | (inlineScriptsAllowed
59 | ? _.template(' ')({
60 | contents: contents
61 | })
62 | : _.template(' ')({
63 | src: rootUrlPathPrefix + pathname
64 | }))
65 | )),
66 |
67 | [
68 | '',
69 | head,
70 | '',
71 | '',
72 | '',
73 | body,
74 | '',
75 | ''
76 | ],
77 | ).join('\n');
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/routes.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Routes
4 |
5 | http://docs.vulcanjs.org/routing.html
6 |
7 | */
8 |
9 | import { Components, addRoute, extendRoute } from 'meteor/vulcan:core';
10 |
11 | // import Home from '../components/common/Home';
12 | // import HowTo from '../components/static/HowTo';
13 | // import About from '../components/static/About';
14 | // import Privacy from '../components/static/Privacy';
15 | // import Terms from '../components/static/Terms';
16 |
17 | // import RoomsSearch from '../components/rooms/RoomsSearch';
18 | // import RoomsPage from '../components/rooms/RoomsPage';
19 | // import RoomsNewPage from '../components/rooms/RoomsNewPage';
20 |
21 | // import BookingsPage from '../components/bookings/BookingsPage';
22 |
23 | // import UsersProfile from '../components/users/UsersProfile';
24 | // import UsersAccount from '../components/users/UsersAccount';
25 |
26 | // import BookingsList from '../components/admin/BookingsList';
27 | // import RoomsList from '../components/admin/RoomsList';
28 |
29 | addRoute([
30 |
31 | {name: 'home', path: '/', componentName: 'Home'},
32 | {name: 'how-to', path: '/how-to', componentName: 'HowTo'},
33 | {name: 'about', path: '/about', componentName: 'About'},
34 | {name: 'privacy', path: '/privacy', componentName: 'Privacy'},
35 | {name: 'terms', path: '/terms', componentName: 'Terms'},
36 |
37 | {name: 'rooms.search', path: '/search', componentName: 'RoomsSearch'},
38 | {name: 'rooms.new', path: '/room/new', componentName: 'RoomsNewPage'},
39 | {name: 'rooms.page', path: '/room/:roomId(/:slug)', componentName: 'RoomsPage'},
40 |
41 | {name: 'bookings.page', path: '/booking/:bookingId', componentName: 'BookingsPage'},
42 | {name: 'bookings.past', path: '/account/bookings/past', componentName: 'BookingsPast'},
43 | {name: 'bookings.current', path: '/account/bookings/current', componentName: 'BookingsCurrent'},
44 | {name: 'bookings.future', path: '/account/bookings/future', componentName: 'BookingsFuture'},
45 | {name: 'bookings.completed', path: '/booking/:bookingId/completed', componentName: 'BookingsCompleted'},
46 |
47 | {name: 'users.single', path:'/users/:slug', componentName: 'UsersProfile'},
48 | {name: 'users.account', path:'/account', componentName: 'UsersAccount'},
49 | {name: 'users.edit', path:'/users/:slug/edit', componentName: 'UsersAccount'},
50 | {name: 'users.signup', path:'/sign-up', componentName: 'UsersSignUp'},
51 | {name: 'users.login', path:'/log-in', componentName: 'UsersLogIn'},
52 |
53 | {name: 'bookings.dashboard', path:'/admin/bookings', componentName: 'BookingsDashboard', layoutName: 'AdminLayout'},
54 | {name: 'rooms.dashboard', path:'/admin/rooms', componentName: 'RoomsDashboard', layoutName: 'AdminLayout'},
55 | {name: 'reviews.dashboard', path:'/admin/reviews', componentName: 'ReviewsDashboard', layoutName: 'AdminLayout'},
56 |
57 | ]);
58 |
59 | extendRoute('admin', { layoutName: 'AdminLayout' });
60 |
--------------------------------------------------------------------------------
/packages/_boilerplate-generator/generator.js:
--------------------------------------------------------------------------------
1 | import { readFile } from 'fs';
2 |
3 | import WebBrowserTemplate from './template-web.browser';
4 | import WebCordovaTemplate from './template-web.cordova';
5 |
6 | // Copied from webapp_server
7 | const readUtf8FileSync = filename => Meteor.wrapAsync(readFile)(filename, 'utf8');
8 |
9 | export class Boilerplate {
10 | constructor(arch, manifest, options = {}) {
11 | this.template = _getTemplate(arch);
12 | this.baseData = null;
13 |
14 | this._generateBoilerplateFromManifest(
15 | manifest,
16 | options
17 | );
18 | }
19 |
20 | // The 'extraData' argument can be used to extend 'self.baseData'. Its
21 | // purpose is to allow you to specify data that you might not know at
22 | // the time that you construct the Boilerplate object. (e.g. it is used
23 | // by 'webapp' to specify data that is only known at request-time).
24 | toHTML(extraData) {
25 | if (!this.baseData || !this.template) {
26 | throw new Error('Boilerplate did not instantiate correctly.');
27 | }
28 |
29 | return "\n" +
30 | this.template({ ...this.baseData, ...extraData });
31 | }
32 |
33 | // XXX Exported to allow client-side only changes to rebuild the boilerplate
34 | // without requiring a full server restart.
35 | // Produces an HTML string with given manifest and boilerplateSource.
36 | // Optionally takes urlMapper in case urls from manifest need to be prefixed
37 | // or rewritten.
38 | // Optionally takes pathMapper for resolving relative file system paths.
39 | // Optionally allows to override fields of the data context.
40 | _generateBoilerplateFromManifest(manifest, {
41 | urlMapper = _.identity,
42 | pathMapper = _.identity,
43 | baseDataExtension,
44 | inline,
45 | } = {}) {
46 |
47 | const boilerplateBaseData = {
48 | css: [],
49 | js: [],
50 | head: '',
51 | body: '',
52 | meteorManifest: JSON.stringify(manifest),
53 | ...baseDataExtension,
54 | };
55 |
56 | _.each(manifest, item => {
57 | const urlPath = urlMapper(item.url);
58 | const itemObj = { url: urlPath };
59 |
60 | if (inline) {
61 | itemObj.scriptContent = readUtf8FileSync(
62 | pathMapper(item.path));
63 | itemObj.inline = true;
64 | }
65 |
66 | if (item.type === 'css' && item.where === 'client') {
67 | boilerplateBaseData.css.push(itemObj);
68 | }
69 |
70 | if (item.type === 'js' && item.where === 'client' &&
71 | // Dynamic JS modules should not be loaded eagerly in the
72 | // initial HTML of the app.
73 | !item.path.startsWith('dynamic/')) {
74 | boilerplateBaseData.js.push(itemObj);
75 | }
76 |
77 | if (item.type === 'head') {
78 | boilerplateBaseData.head =
79 | readUtf8FileSync(pathMapper(item.path));
80 | }
81 |
82 | if (item.type === 'body') {
83 | boilerplateBaseData.body =
84 | readUtf8FileSync(pathMapper(item.path));
85 | }
86 | });
87 |
88 | this.baseData = boilerplateBaseData;
89 | }
90 | };
91 |
92 | // Returns a template function that, when called, produces the boilerplate
93 | // html as a string.
94 | const _getTemplate = arch => {
95 | if (arch === 'web.browser') {
96 | return WebBrowserTemplate;
97 | } else if (arch === 'web.cordova') {
98 | return WebCordovaTemplate;
99 | } else {
100 | throw new Error('Unsupported arch: ' + arch);
101 | }
102 | };
103 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Vulcan",
3 | "version": "1.7.0",
4 | "engines": {
5 | "npm": "^3.0"
6 | },
7 | "scripts": {
8 | "prestart": "sh prestart_vulcan.sh",
9 | "start": "meteor --settings settings.json",
10 | "lint": "eslint --cache --ext .jsx,js packages"
11 | },
12 | "dependencies": {
13 | "@google/maps": "^0.4.3",
14 | "analytics-node": "^2.1.1",
15 | "apollo-client": "^1.2.2",
16 | "apollo-errors": "^1.4.0",
17 | "babel-runtime": "^6.26.0",
18 | "bluebird": "^3.5.0",
19 | "body-parser": "^1.15.2",
20 | "classnames": "^2.2.3",
21 | "cookie-parser": "^1.4.3",
22 | "crypto-js": "^3.1.9-1",
23 | "dataloader": "^1.3.0",
24 | "deepmerge": "^1.2.0",
25 | "escape-string-regexp": "^1.0.5",
26 | "express": "^4.14.0",
27 | "flat": "^4.0.0",
28 | "formsy-react": "^0.19.5",
29 | "formsy-react-components": "^0.10.1",
30 | "google-map-react": "^0.24.0",
31 | "graphql": "^0.9.6",
32 | "graphql-anywhere": "^3.0.1",
33 | "graphql-date": "^1.0.2",
34 | "graphql-server-express": "^0.6.0",
35 | "graphql-tag": "^2.0.0",
36 | "graphql-tools": "^0.10.1",
37 | "graphql-type-json": "^0.1.4",
38 | "handlebars": "^4.0.5",
39 | "he": "^1.1.1",
40 | "history": "^3.0.0",
41 | "html-to-text": "^2.1.0",
42 | "immutability-helper": "^2.0.0",
43 | "import": "0.0.6",
44 | "intl": "^1.2.4",
45 | "intl-locales-supported": "^1.0.0",
46 | "isomorphic-fetch": "^2.2.1",
47 | "juice": "^1.11.0",
48 | "mailchimp": "^1.1.6",
49 | "marked": "^0.3.5",
50 | "metascraper": "^1.0.6",
51 | "meteor-node-stubs": "^0.2.3",
52 | "mingo": "^0.8.1",
53 | "moment": "^2.13.0",
54 | "optics-agent": "^1.0.5",
55 | "prop-types": "^15.5.10",
56 | "react": "^15.6.1",
57 | "react-addons-pure-render-mixin": "^15.4.1",
58 | "react-apollo": "^1.1.1",
59 | "react-bootstrap": "^0.30.7",
60 | "react-bootstrap-datetimepicker": "0.0.22",
61 | "react-cookie": "^0.4.6",
62 | "react-datetime": "^2.3.2",
63 | "react-dom": "^15.4.1",
64 | "react-dropzone": "^3.12.2",
65 | "react-helmet": "^5.1.3",
66 | "react-intl": "^2.1.3",
67 | "react-loadable": "^4.0.3",
68 | "react-places-autocomplete": "^5.0.0",
69 | "react-redux": "^5.0.1",
70 | "react-router": "^3.0.0",
71 | "react-router-bootstrap": "^0.23.1",
72 | "react-router-scroll": "^0.4.1",
73 | "react-stripe-checkout": "2.6.3",
74 | "recompose": "^0.21.2",
75 | "redux": "^3.6.0",
76 | "rss": "^1.2.1",
77 | "sanitize-html": "^1.11.4",
78 | "sendy-api": "^0.1.0",
79 | "simpl-schema": "^0.2.3",
80 | "speakingurl": "^9.0.0",
81 | "stripe": "^4.23.1",
82 | "styled-components": "^2.1.1",
83 | "tracker-component": "^1.3.14",
84 | "underscore": "^1.8.3",
85 | "url": "^0.11.0"
86 | },
87 | "private": true,
88 | "devDependencies": {
89 | "autoprefixer": "^6.3.6",
90 | "babel-eslint": "^7.0.0",
91 | "eslint": "^3.10.1",
92 | "eslint-config-airbnb": "^13.0.0",
93 | "eslint-config-meteor": "0.0.9",
94 | "eslint-import-resolver-meteor": "^0.3.3",
95 | "eslint-plugin-babel": "^3.3.0",
96 | "eslint-plugin-import": "^2.2.0",
97 | "eslint-plugin-jsx-a11y": "^2.2.3",
98 | "eslint-plugin-meteor": "^4.0.1",
99 | "eslint-plugin-react": "^6.7.1"
100 | },
101 | "postcss": {
102 | "plugins": {
103 | "autoprefixer": {
104 | "browsers": [
105 | "last 2 versions"
106 | ]
107 | }
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/users/UsersMenu.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | User menu (when logged in)
4 |
5 | */
6 |
7 | import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core';
8 | import Users from 'meteor/vulcan:users';
9 | import React from 'react';
10 | import PropTypes from 'prop-types';
11 | import { FormattedMessage } from 'meteor/vulcan:i18n';
12 | import { Meteor } from 'meteor/meteor';
13 | import Dropdown from 'react-bootstrap/lib/Dropdown';
14 | import MenuItem from 'react-bootstrap/lib/MenuItem';
15 | import { LinkContainer } from 'react-router-bootstrap';
16 | import { withApollo } from 'react-apollo';
17 |
18 | const UsersMenu = ({currentUser, client}) =>
19 |
20 |
21 |
22 |
23 | {Users.getDisplayName(currentUser)}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | {Users.canDo(currentUser, 'rooms.new') ?
43 |
44 |
45 |
46 | : null}
47 |
48 | {Users.canDo(currentUser, 'users.view.all') ?
49 |
50 |
51 |
52 | : null}
53 |
54 | {Users.canDo(currentUser, 'bookings.view.all') ?
55 |
56 |
57 |
58 | : null}
59 |
60 | {Users.canDo(currentUser, 'rooms.view.all') ?
61 |
62 |
63 |
64 | : null}
65 |
66 | {Users.canDo(currentUser, 'reviews.view.all') ?
67 |
68 |
69 |
70 | : null}
71 |
72 | Meteor.logout(() => client.resetStore())}>
73 |
74 |
75 |
76 |
77 |
78 | UsersMenu.propsTypes = {
79 | currentUser: PropTypes.object,
80 | client: PropTypes.object,
81 | };
82 |
83 | registerComponent('UsersMenu', UsersMenu, withApollo, withCurrentUser);
84 |
85 | // export default withCurrentUser(withApollo(UsersMenu));
86 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ZensRoom
2 |
3 | This is an open-source AirBnB-type app built using [VulcanJS](http://vulcanjs.org) and developed as prototype for [Zens](http://www.zens.tokyo/), a Tokyo-based company.
4 |
5 | ## Install
6 |
7 | 1. Clone this repo
8 | 2. Follow the [VulcanJS install instructions](http://docs.vulcanjs.org/#Install).
9 | 3. Run with `npm start` (or the equivalent `meteor --settings settings.json`).
10 |
11 | ## Local Development
12 |
13 | By default, the app will look for VulcanJS core packages (`vulcan:core`, `vulcan:email`, `vulcan:forms`, etc.) on [Atmosphere](https://atmospherejs.com/), Meteor's package server.
14 |
15 | For local development, it can be useful to have access to the VulcanJS codebase locally and be able to modify it if needed. You can do so by following these steps:
16 |
17 | 1. Clone the main [VulcanJS repo](https://github.com/VulcanJS/Vulcan) locally (for example, to `~/Vulcan`).
18 | 2. Inside the main repo, checkout the `devel` branch.
19 | 3. Go back to the ZensRoom directory and launch your app with:
20 |
21 | ```
22 | METEOR_PACKAGE_DIRS="~/Vulcan/packages" meteor --port 3000 --settings settings.json
23 | ```
24 |
25 | Note that if you'd like, you can create an alias for that command in your `.bash_profile` file:
26 |
27 | ```
28 | alias runvulcan='METEOR_PACKAGE_DIRS="~/Vulcan/packages" meteor --port 3000 --settings settings.json'
29 | ```
30 |
31 | ## Settings
32 |
33 | This project expects a few specific API keys, as defined in your project's `settings.json` (in addition to any other generic VulcanJS settings you might already have). Here's a sample file:
34 |
35 | ```
36 | {
37 | "public": {
38 | "title": "Your Site Name",
39 | "tagline":"Your site tagline",
40 |
41 | "language": "en",
42 | "locale": "en",
43 |
44 | "cloudinary": {
45 | "cloudName": "123foo"
46 | },
47 |
48 | "stripe": {
49 | "publishableKeyTest": "pk_test_123foo"
50 | },
51 |
52 | "googlemaps": {
53 | "apiKey": "456foo"
54 | }
55 | },
56 |
57 | "stripe": {
58 | "secretKeyTest": "sk_test_123foo"
59 | },
60 |
61 | "defaultEmail": "hello@foo.com",
62 | "mailUrl": "smtp://username%40yourdomain.mailgun.org:yourpassword123@smtp.mailgun.org:587/",
63 |
64 | "oAuth": {
65 | "twitter": {
66 | "consumerKey": "foo",
67 | "secret": "bar"
68 | },
69 | "facebook": {
70 | "appId": "foo",
71 | "secret": "bar"
72 | }
73 | }
74 | }
75 | ```
76 |
77 | ## Dependencies
78 |
79 | The ZensRoom app depends on the following VulcanJS [packages](https://github.com/SachaG/Zensroom/blob/devel/packages/zensroom/package.js#L10-L19):
80 |
81 | - `vulcan:core`: VulcanJS core features.
82 | - `vulcan:forms`: [SmartForms](http://docs.vulcanjs.org/forms.html) component.
83 | - `vulcan:forms-upload`: Image upload form component (using [Cloudinary](http://cloudinary.com)).
84 | - `vulcan:accounts`: User accounts UI (log in/sign up/reset password/etc.).
85 | - `vulcan:payments`: [Payments package](http://docs.vulcanjs.org/payments.html) using Stripe.
86 | - `vulcan:admin`: [Admin](http://docs.vulcanjs.org/admin.html) dashboard.
87 | - `vulcan:maps`: Maps component.
88 |
89 | Packages in the repo not mentioned above (`vulcan:posts`, `vulcan:comments`, etc.) are not currently used by this project but might be in the future.
90 |
91 | See also `package.json` for a list of NPM dependencies.
92 |
93 | ## Architecture
94 |
95 | The code for the app is available in `/packages/zensroom`, split into the following directories:
96 |
97 | - `client`: contains the client entry point and any client-specific code.
98 | - `server`: contains the server entry point and any server-specific code.
99 | - `components`: contains all React components.
100 | - `containers`: contains all React containers.
101 | - `modules`: contains all other JavaScript modules.
102 |
103 | ## Collections (Models)
104 |
105 | The app uses the following collections:
106 |
107 | - `Rooms`
108 | - `Bookings`
109 | - `Reviews`
110 | - `Users`
111 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/rooms/RoomsSearchForm.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Room search form
4 |
5 | */
6 |
7 | import React, { Component } from 'react';
8 | import { Components, registerComponent, getSetting } from 'meteor/vulcan:core';
9 | import { Form, Input } from 'formsy-react-components';
10 | import { withRouter } from 'react-router'
11 | import DateTimePicker from 'react-datetime';
12 | import Button from 'react-bootstrap/lib/Button';
13 | import { FormattedMessage } from 'meteor/vulcan:i18n';
14 | import moment from 'moment';
15 |
16 | class RoomsSearchForm extends Component {
17 | constructor(props) {
18 | super(props);
19 | this.updateFromDate = this.updateFromDate.bind(this);
20 | this.updateToDate = this.updateToDate.bind(this);
21 | this.submitForm = this.submitForm.bind(this);
22 |
23 | const state = {};
24 | if (props.location.query.from) {
25 | state.from = moment(props.location.query.from, 'YYYY-MM-DD');
26 | }
27 | if (props.location.query.to) {
28 | state.to = moment(props.location.query.to, 'YYYY-MM-DD');
29 | }
30 | if (props.location.query.location) {
31 | state.location = decodeURIComponent(props.location.query.location);
32 | }
33 | this.state = state;
34 | }
35 |
36 | updateFromDate(date) {
37 | this.setState({ from: date });
38 | }
39 |
40 | updateToDate(date) {
41 | this.setState({ to: date });
42 | }
43 |
44 | async submitForm({ location }) {
45 |
46 | let query = '';
47 |
48 | if (this.state.from) {
49 | query += `from=${this.state.from.format('YYYY-MM-DD')}`;
50 | }
51 |
52 | if (this.state.to) {
53 | query += `&to=${this.state.to.format('YYYY-MM-DD')}`;
54 | }
55 |
56 | if (location) {
57 | const geocodeUrl = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(location)}&key=${getSetting('googlemaps').apiKey}`
58 |
59 | const response = await fetch(geocodeUrl);
60 | const geoData = await response.json();
61 | console.log(geoData)
62 | const results = geoData.results[0];
63 | query += `&location=${encodeURIComponent(location)}&lng=${results.geometry.location.lng}&lat=${results.geometry.location.lat}&type=${results.types[0]}`
64 | }
65 |
66 | this.props.router.push(`/search?${query}`);
67 | }
68 |
69 | render() {
70 |
71 | return (
72 |
106 | );
107 | }
108 |
109 | }
110 |
111 | registerComponent('RoomsSearchForm', RoomsSearchForm, withRouter);
112 |
113 | // export default withRouter(RoomsSearchForm);
--------------------------------------------------------------------------------
/packages/zensroom/lib/server/emails/common/wrapper.handlebars:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{siteName}}
8 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/stylesheets/_rooms.scss:
--------------------------------------------------------------------------------
1 | //////////////////////////////////////////////////////
2 | // Rooms Search Form //
3 | //////////////////////////////////////////////////////
4 |
5 | .rooms-search-form{
6 | padding: $spacing;
7 | margin-bottom: $spacing;
8 | .form-horizontal{
9 | display: flex;
10 | }
11 | }
12 |
13 | .rooms-search-form-field{
14 | flex: 1;
15 | margin-right: $spacing/2;
16 | display: flex;
17 | flex-direction: column;
18 | justify-content: flex-end;
19 | &:last-child{
20 | margin-right: 0;
21 | }
22 | }
23 |
24 | .rooms-search-form-submit{
25 | width: 100%;
26 | }
27 |
28 | //////////////////////////////////////////////////////
29 | // Rooms Grid //
30 | //////////////////////////////////////////////////////
31 |
32 | .rooms-grid{
33 | display: flex;
34 | flex-wrap: wrap;
35 | .rooms-item{
36 | width: calc((100% - #{$spacing * 2})/3);
37 | margin-right: $spacing;
38 | margin-bottom: $spacing;
39 | &:nth-child(3n){
40 | margin-right: 0;
41 | }
42 | }
43 | }
44 |
45 | .rooms-grid-load-more{
46 | border-radius: 3px;
47 | border: 1px solid $light-grey;
48 | padding: $spacing;
49 | display: block;
50 | text-align: center;
51 | }
52 |
53 | //////////////////////////////////////////////////////
54 | // Rooms Item //
55 | //////////////////////////////////////////////////////
56 |
57 | .rooms-item{
58 |
59 | }
60 |
61 | .rooms-item-image{
62 | position: relative;
63 | margin-bottom: $spacing/2;
64 | img{
65 | display: block;
66 | width: 100%;
67 | }
68 | }
69 |
70 | .rooms-item-price{
71 | position: absolute;
72 | top: 0;
73 | left: 0;
74 | bottom: 0;
75 | right: 0;
76 | display: flex;
77 | flex-direction: column;
78 | justify-content: flex-end;
79 | background: linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,0) 50%, rgba(0,0,0,0.6) 80%);
80 | div{
81 | color: white;
82 | display: block;
83 | padding: 10px;
84 | }
85 | }
86 |
87 | .rooms-item-name{
88 | font-size: 1rem;
89 | }
90 |
91 | .rooms-item-city{
92 | font-size: 0.8rem;
93 | }
94 |
95 | //////////////////////////////////////////////////////
96 | // Rooms Page //
97 | //////////////////////////////////////////////////////
98 |
99 | .rooms-contents{
100 | display: flex;
101 | }
102 |
103 | .rooms-main{
104 | flex: 1;
105 | margin-right: $spacing*2;
106 | }
107 |
108 | .rooms-section{
109 | margin-bottom: $spacing * 2;
110 | }
111 |
112 | .rooms-hero-image{
113 | margin-bottom: $spacing*2;
114 | img{
115 | display: block;
116 | width: 100%;
117 | cursor: pointer;
118 | }
119 | }
120 |
121 | .rooms-photos-modal{
122 | display: flex;
123 | justify-content: center;
124 | align-items: center;
125 | max-width: 900px;
126 | .modal-content{
127 | background: none;
128 | border: none;
129 | }
130 | .rooms-photos{
131 | position: relative;
132 | }
133 | }
134 |
135 | .rooms-photos-previous, .rooms-photos-next{
136 | position: absolute;
137 | top: 50%;
138 | margin-top: -20px;
139 | height: 40px;
140 | width: 40px;
141 | display: flex;
142 | justify-content: center;
143 | align-items: center;
144 | .icon{
145 | display: block;
146 | color: white;
147 | font-size: 5rem;
148 | }
149 | &:hover, &:focus, &:visited{
150 | color: white;
151 | text-decoration: none;
152 | }
153 | }
154 | .rooms-photos-previous{
155 | left: -60px;
156 | }
157 | .rooms-photos-next{
158 | right: -60px;
159 | }
160 |
161 | .rooms-name{
162 | margin-bottom: $spacing/2;
163 | }
164 | .rooms-city{
165 | margin-bottom: $spacing/2;
166 | font-size: 1rem;
167 | }
168 | .rooms-description{
169 | margin-bottom: $spacing/2;
170 | }
171 | .rooms-amenities{
172 | margin-bottom: $spacing/2;
173 | }
174 |
175 | .rooms-sidebar{
176 | max-width: 30%;
177 | }
178 |
179 | .rooms-book{
180 | margin-bottom: $spacing;
181 | }
182 |
183 | //////////////////////////////////////////////////////
184 | // Rooms Search Results //
185 | //////////////////////////////////////////////////////
186 |
187 | .rooms-search-results-map{
188 | margin-bottom: $spacing;
189 | }
190 |
191 | //////////////////////////////////////////////////////
192 | // Rooms Filters //
193 | //////////////////////////////////////////////////////
194 |
195 | .rooms-search-filters{
196 | margin-bottom: $spacing;
197 | }
198 |
199 | .rooms-search-filters-list{
200 | list-style-type: none;
201 | padding: 0;
202 | margin: 0;
203 | display: flex;
204 | flex-wrap: wrap;
205 | }
206 |
207 | .rooms-search-filter{
208 | width: calc((100% - #{$spacing * 2})/3);
209 | margin-right: $spacing;
210 | margin-bottom: $spacing/2;
211 | font-size: 0.9rem;
212 | &:nth-child(3n) {
213 | margin-right: 0;
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/bookings/schema.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | The Bookings schema
4 |
5 | http://docs.vulcanjs.org/schemas.html#Schemas
6 |
7 | */
8 |
9 | import moment from 'moment';
10 | import { Utils } from 'meteor/vulcan:core';
11 | import Rooms from '../rooms/collection.js';
12 |
13 | const schema = {
14 | // default properties
15 |
16 | _id: {
17 | type: String,
18 | optional: true,
19 | viewableBy: ['members'],
20 | },
21 | createdAt: {
22 | type: Date,
23 | optional: true,
24 | viewableBy: ['members'],
25 | onInsert: (document, currentUser) => {
26 | return new Date();
27 | }
28 | },
29 | userId: {
30 | type: String,
31 | optional: true,
32 | viewableBy: ['members'],
33 | resolveAs: {
34 | fieldName: 'user',
35 | type: 'User',
36 | resolver: async (booking, args, { Users, currentUser }) => {
37 | const user = await Users.loader.load(booking.userId);
38 | return Users.restrictViewableFields(currentUser, Users, user);
39 | },
40 | addOriginalField: true
41 | }
42 | },
43 |
44 | roomId: {
45 | type: String,
46 | viewableBy: ['members'],
47 | insertableBy: ['members'],
48 | hidden: true,
49 | resolveAs: {
50 | fieldName: 'room',
51 | type: 'Room',
52 | resolver: async (booking, args, { Rooms, Users, currentUser }) => {
53 | const room = await Rooms.loader.load(booking.roomId);
54 | return Users.restrictViewableFields(currentUser, Rooms, room);
55 | },
56 | addOriginalField: true
57 | },
58 | },
59 |
60 | startAt: {
61 | label: 'Check In Date',
62 | type: Date,
63 | viewableBy: ['members'],
64 | insertableBy: ['members'],
65 | editableBy: ['admins'],
66 | control: 'datetime',
67 | },
68 |
69 | endAt: {
70 | label: 'Check Out Date',
71 | type: Date,
72 | viewableBy: ['members'],
73 | insertableBy: ['members'],
74 | editableBy: ['admins'],
75 | control: 'datetime',
76 | },
77 |
78 | numberOfGuests: {
79 | label: 'Guests',
80 | type: Number,
81 | viewableBy: ['members'],
82 | insertableBy: ['members'],
83 | editableBy: ['admins'],
84 | },
85 |
86 | amount: {
87 | label: 'Amount',
88 | type: Number,
89 | optional: true,
90 | viewableBy: ['members'],
91 | insertableBy: ['admins'],
92 | editableBy: ['admins'],
93 | onInsert: async document => {
94 | const room = await Rooms.queryOne(document.roomId, { fragmentName: 'RoomsItemFragment' });
95 | const numberOfNights = moment(document.endAt).diff(moment(document.startAt), 'days');
96 | const amount = room.pricePerNight * document.numberOfGuests * numberOfNights;
97 | return amount;
98 | },
99 | },
100 |
101 | paidAt: {
102 | type: Date,
103 | optional: true,
104 | viewableBy: ['members'],
105 | },
106 |
107 | status: {
108 | type: Number,
109 | optional: true,
110 | viewableBy: ['members'],
111 | insertableBy: ['admins'],
112 | editableBy: ['admins'],
113 | control: 'select',
114 | form: {
115 | options: () => {
116 | return [
117 | {
118 | value: 1,
119 | label: 'pending'
120 | },
121 | {
122 | value: 2,
123 | label: 'approved'
124 | },
125 | {
126 | value: 3,
127 | label: 'paid'
128 | },
129 | {
130 | value: 4,
131 | label: 'rejected'
132 | },
133 | ]
134 | },
135 | },
136 | onInsert: document => {
137 | return document.status || 1;
138 | },
139 | },
140 |
141 | // GraphQL-only fields
142 |
143 | pageUrl: {
144 | type: String,
145 | optional: true,
146 | resolveAs: {
147 | type: 'String',
148 | resolver: (booking, args, context) => {
149 | return `${Utils.getSiteUrl()}booking/${booking._id}`;
150 | },
151 | }
152 | },
153 |
154 | startAtFormatted: {
155 | label: 'Check In',
156 | type: String,
157 | optional: true,
158 | resolveAs: {
159 | type: 'String',
160 | resolver: (booking, args, context) => {
161 | return moment(booking.startAt).format('dddd, MMMM Do YYYY');
162 | }
163 | }
164 | },
165 |
166 | endAtFormatted: {
167 | label: 'Check Out',
168 | type: String,
169 | optional: true,
170 | resolveAs: {
171 | type: 'String',
172 | resolver: (booking, args, context) => {
173 | return moment(booking.endAt).format('dddd, MMMM Do YYYY');
174 | }
175 | }
176 | },
177 |
178 | paidAtFormatted: {
179 | label: 'Paid At',
180 | type: String,
181 | optional: true,
182 | resolveAs: {
183 | type: 'String',
184 | resolver: (booking, args, context) => {
185 | return booking.paidAt && moment(booking.paidAt).format('dddd, MMMM Do YYYY');
186 | }
187 | }
188 | },
189 |
190 | startAtFormattedShort: {
191 | label: 'Check In',
192 | type: String,
193 | optional: true,
194 | resolveAs: {
195 | type: 'String',
196 | resolver: (booking, args, context) => {
197 | return moment(booking.startAt).format('MM/DD/YY');
198 | }
199 | }
200 | },
201 |
202 | endAtFormattedShort: {
203 | label: 'Check Out',
204 | type: String,
205 | optional: true,
206 | resolveAs: {
207 | type: 'String',
208 | resolver: (booking, args, context) => {
209 | return moment(booking.endAt).format('MM/DD/YY');
210 | }
211 | }
212 | },
213 |
214 | paidAtFormattedShort: {
215 | label: 'Paid At',
216 | type: String,
217 | optional: true,
218 | resolveAs: {
219 | type: 'String',
220 | resolver: (booking, args, context) => {
221 | return booking.paidAt && moment(booking.paidAt).format('MM/DD/YY');
222 | }
223 | }
224 | },
225 |
226 | };
227 |
228 | export default schema;
--------------------------------------------------------------------------------
/packages/zensroom/lib/components/bookings/BookingsNewForm.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Form for inserting a new booking, wrapped with withNew HoC.
4 |
5 | http://docs.vulcanjs.org/mutations.html#Higher-Order-Components
6 |
7 | */
8 |
9 | import React, { Component } from 'react';
10 | import { Components, registerComponent, withCurrentUser, getFragment, getSetting, withMessages, withNew, addCallback } from 'meteor/vulcan:core';
11 | import { withRouter } from 'react-router';
12 | import compose from 'recompose/compose';
13 | import DateTimePicker from 'react-datetime';
14 | import Button from 'react-bootstrap/lib/Button';
15 | import gql from 'graphql-tag';
16 | import moment from 'moment';
17 | import { intlShape, FormattedMessage } from 'meteor/vulcan:i18n';
18 | import { Form, Input } from 'formsy-react-components';
19 |
20 | import Bookings from '../../modules/bookings/collection';
21 | import withUnavailableDates from '../../containers/withUnavailableDatesContainer';
22 |
23 | class BookingsNewForm extends Component {
24 |
25 | constructor() {
26 | super();
27 |
28 | this.success = this.success.bind(this);
29 | this.submitForm = this.submitForm.bind(this);
30 | this.updateFromDate = this.updateFromDate.bind(this);
31 | this.updateToDate = this.updateToDate.bind(this);
32 | this.updateGuests = this.updateGuests.bind(this);
33 | this.isAvailable = this.isAvailable.bind(this);
34 | this.createNewBooking = this.createNewBooking.bind(this);
35 |
36 | this.state = {
37 | from: null,
38 | to: null,
39 | numberOfGuests: 1,
40 | disabled: false
41 | }
42 | }
43 |
44 | updateFromDate(date) {
45 | this.setState({ from: date });
46 | if (date > this.state.to) {
47 | this.setState({
48 | to: date.clone().add('days', 1)
49 | })
50 | }
51 | }
52 |
53 | updateToDate(date) {
54 | this.setState({ to: date });
55 | }
56 |
57 | updateGuests(name, value) {
58 | this.setState({
59 | numberOfGuests: parseInt(value || 1)
60 | });
61 | }
62 |
63 | /*
64 |
65 | Helper to tell if a date is available
66 |
67 | */
68 | isAvailable(mDate) {
69 | const unavailableDates = this.props.unavailableDates && this.props.unavailableDates.map(date => moment(new Date(date)).startOf('day').toString());
70 | return !_.contains(unavailableDates, mDate.toString())
71 | }
72 |
73 | /*
74 |
75 | Form submit handler
76 |
77 | */
78 | submitForm(data) {
79 |
80 | // disable form to prevent multiple submissions
81 | this.setState({ disabled: true });
82 |
83 | // if fields are missing, show message and abort submission
84 | if (!this.state.from || !this.state.to || !this.state.numberOfGuests) {
85 | alert(this.context.intl.formatMessage({id: 'bookings.please_fill_in_all_fields'}));
86 | this.setState({ disabled: false });
87 | return;
88 | }
89 |
90 | // create alias for this.createNewBooking
91 | const createNewBooking = this.createNewBooking;
92 |
93 | // create callback function and set it to only run once
94 | function createNewBookingCallback() {
95 | createNewBooking(data);
96 | return {};
97 | }
98 | createNewBookingCallback.runOnce = true;
99 |
100 | if (this.props.currentUser) { // user is logged in
101 |
102 | this.createNewBooking(data);
103 |
104 | } else { // user is not logged in
105 |
106 | // add postlogin callback, go to sign-up page, show message
107 | addCallback('users.postlogin', createNewBookingCallback);
108 | this.props.router.push('/sign-up');
109 | this.props.flash(this.context.intl.formatMessage({id: 'users.please_sign_up_log_in'}), 'error');
110 |
111 | }
112 | }
113 |
114 | /*
115 |
116 | Trigger new booking mutation and then call this.success()
117 |
118 | */
119 | createNewBooking(data) {
120 | console.log('// createNewBooking')
121 | console.log(data)
122 | this.props.newMutation({document: {
123 | startAt: this.state.from.toDate(),
124 | endAt: this.state.to.toDate(),
125 | numberOfGuests: this.state.numberOfGuests,
126 | roomId: this.props.room._id
127 | }}).then(result => this.success(result.data.BookingsNew));
128 | }
129 |
130 | /*
131 |
132 | Success callback
133 |
134 | */
135 | success(booking) {
136 | this.props.router.push({pathname: `/booking/${booking._id}`});
137 | this.props.flash(this.context.intl.formatMessage({id: 'bookings.created'}), 'success');
138 | }
139 |
140 | /*
141 |
142 | Render
143 |
144 | */
145 | render() {
146 |
147 | const numberOfNights = this.state.from && this.state.to ? this.state.to.diff(this.state.from, 'days') : 0;
148 | const totalPrice = this.props.room.pricePerNight * this.state.numberOfGuests * numberOfNights;
149 |
150 | return (
151 |
195 |
196 | )
197 | }
198 | }
199 |
200 | BookingsNewForm.contextTypes = {
201 | intl: intlShape
202 | };
203 |
204 | const options = {
205 | collection: Bookings,
206 | fragment: gql`
207 | fragment BookingFragment on Booking {
208 | __typename
209 | _id
210 | createdAt
211 | userId
212 | roomId
213 | startAt
214 | endAt
215 | paidAt
216 | }
217 | `
218 | }
219 |
220 | registerComponent('BookingsNewForm', BookingsNewForm, [withNew, options], withRouter, withMessages, withCurrentUser, withUnavailableDates);
221 |
222 | // export default compose(
223 | // withNew(options),
224 | // withRouter,
225 | // withMessages,
226 | // withCurrentUser,
227 | // withUnavailableDates,
228 | // )(BookingsNewForm)
229 |
--------------------------------------------------------------------------------
/packages/zensroom/lib/modules/rooms/schema.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Rooms schema
4 |
5 | http://docs.vulcanjs.org/schemas.html#Schemas
6 |
7 | */
8 |
9 | import FormsUpload from 'meteor/vulcan:forms-upload';
10 | import { amenities, spaces } from '../data';
11 | import { Utils } from 'meteor/vulcan:core';
12 |
13 | const formGroups = {
14 | infos: {
15 | name: 'infos',
16 | label: 'Infos',
17 | order: 10,
18 | },
19 | photos: {
20 | name: 'photos',
21 | label: 'Photos',
22 | order: 20,
23 | startCollapsed: true
24 | },
25 | amenities: {
26 | name: 'amenities',
27 | label: 'Amenities',
28 | order: 30,
29 | startCollapsed: true
30 | },
31 | address: {
32 | name: 'address',
33 | label: 'Address',
34 | order: 40,
35 | startCollapsed: true
36 | },
37 | price: {
38 | name: 'price',
39 | label: 'Price',
40 | order: 50,
41 | startCollapsed: true
42 | }
43 | };
44 |
45 | const schema = {
46 |
47 | // default properties
48 |
49 | _id: {
50 | type: String,
51 | optional: true,
52 | viewableBy: ['guests'],
53 | },
54 |
55 | createdAt: {
56 | type: Date,
57 | optional: true,
58 | viewableBy: ['guests'],
59 | onInsert: (document, currentUser) => {
60 | return new Date();
61 | }
62 | },
63 |
64 | userId: {
65 | type: String,
66 | optional: true,
67 | viewableBy: ['guests'],
68 | resolveAs: {
69 | fieldName: 'user',
70 | type: 'User',
71 | resolver: (movie, args, context) => {
72 | return context.Users.findOne({ _id: movie.userId }, { fields: context.Users.getViewableFields(context.currentUser, context.Users) });
73 | },
74 | addOriginalField: true
75 | }
76 | },
77 |
78 | // custom properties
79 |
80 | roomType: {
81 | label: 'Room Type',
82 | type: String,
83 | optional: false,
84 | viewableBy: ['guests'],
85 | insertableBy: ['members'],
86 | editableBy: ['members'],
87 | control: 'select',
88 | form: {
89 | options: [
90 | {label: 'Entire Place', value: 'place'},
91 | {label: 'Private Room', value: 'private'},
92 | {label: 'Shared Room', value: 'shared'},
93 | ]
94 | },
95 | group: formGroups.info
96 | },
97 |
98 | propertyType: {
99 | label: 'Type',
100 | type: String,
101 | optional: false,
102 | viewableBy: ['guests'],
103 | insertableBy: ['members'],
104 | editableBy: ['members'],
105 | control: 'select',
106 | form: {
107 | options: [
108 | {label: 'Apartment', value: 'apartment'},
109 | {label: 'House', value: 'house'},
110 | {label: 'Other', value: 'other'},
111 | ]
112 | },
113 | group: formGroups.info
114 | },
115 |
116 | name: {
117 | label: 'Name',
118 | type: String,
119 | optional: false,
120 | viewableBy: ['guests'],
121 | insertableBy: ['members'],
122 | editableBy: ['members'],
123 | group: formGroups.info,
124 | searchable: true,
125 | limit: 90,
126 | max: 90
127 | },
128 |
129 | description: {
130 | label: 'Description',
131 | type: String,
132 | optional: false,
133 | viewableBy: ['guests'],
134 | insertableBy: ['members'],
135 | editableBy: ['members'],
136 | control: 'textarea',
137 | group: formGroups.info,
138 | searchable: true,
139 | limit: 300,
140 | max: 300
141 | },
142 |
143 | rules: {
144 | label: 'House Rules',
145 | type: String,
146 | optional: false,
147 | viewableBy: ['guests'],
148 | insertableBy: ['members'],
149 | editableBy: ['members'],
150 | control: 'textarea',
151 | group: formGroups.info
152 | },
153 |
154 | bedsNumber: {
155 | label: 'Number of Beds',
156 | type: Number,
157 | optional: false,
158 | viewableBy: ['guests'],
159 | insertableBy: ['members'],
160 | editableBy: ['members'],
161 | group: formGroups.info,
162 | control: 'number'
163 | },
164 |
165 | guestsNumber: {
166 | label: 'Number of Guests',
167 | type: Number,
168 | optional: false,
169 | viewableBy: ['guests'],
170 | insertableBy: ['members'],
171 | editableBy: ['members'],
172 | group: formGroups.info,
173 | control: 'number'
174 | },
175 |
176 | photos: {
177 | label: 'Photos',
178 | type: Array,
179 | optional: false,
180 | viewableBy: ['guests'],
181 | insertableBy: ['members'],
182 | editableBy: ['members'],
183 | control: FormsUpload, // use the FormsUpload form component
184 | form: {
185 | options: {
186 | preset: 'zensroom'
187 | },
188 | },
189 | group: formGroups.photos
190 | },
191 |
192 | 'photos.$': {
193 | type: Object,
194 | blackbox: true,
195 | optional: true,
196 | },
197 |
198 | amenities: {
199 | label: 'Amenities',
200 | type: Array,
201 | optional: true,
202 | viewableBy: ['guests'],
203 | insertableBy: ['members'],
204 | editableBy: ['members'],
205 | control: 'checkboxgroup',
206 | form: {
207 | options: amenities
208 | },
209 | group: formGroups.amenities
210 | },
211 |
212 | 'amenities.$': {
213 | type: String,
214 | optional: true,
215 | },
216 |
217 | spaces: {
218 | label: 'Spaces',
219 | type: Array,
220 | optional: true,
221 | viewableBy: ['guests'],
222 | insertableBy: ['members'],
223 | editableBy: ['members'],
224 | control: 'checkboxgroup',
225 | form: {
226 | options: spaces
227 | },
228 | group: formGroups.amenities
229 | },
230 |
231 | 'spaces.$': {
232 | type: String,
233 | optional: true,
234 | },
235 |
236 | country: {
237 | type: String,
238 | optional: true,
239 | viewableBy: ['guests'],
240 | insertableBy: ['members'],
241 | editableBy: ['members'],
242 | group: formGroups.address
243 | },
244 |
245 | zipCode: {
246 | type: String,
247 | optional: true,
248 | viewableBy: ['guests'],
249 | insertableBy: ['members'],
250 | editableBy: ['members'],
251 | group: formGroups.address
252 | },
253 |
254 | state: {
255 | type: String,
256 | optional: true,
257 | viewableBy: ['guests'],
258 | insertableBy: ['members'],
259 | editableBy: ['members'],
260 | group: formGroups.address
261 | },
262 |
263 | city: {
264 | type: String,
265 | optional: true,
266 | viewableBy: ['guests'],
267 | insertableBy: ['members'],
268 | editableBy: ['members'],
269 | group: formGroups.address
270 | },
271 |
272 | address: {
273 | type: String,
274 | optional: true,
275 | viewableBy: ['guests'],
276 | insertableBy: ['members'],
277 | editableBy: ['members'],
278 | group: formGroups.address
279 | },
280 |
281 | address2: {
282 | type: String,
283 | optional: true,
284 | viewableBy: ['guests'],
285 | insertableBy: ['members'],
286 | editableBy: ['members'],
287 | group: formGroups.address
288 | },
289 |
290 | geoData: {
291 | type: Object,
292 | blackbox: true,
293 | optional: true,
294 | },
295 |
296 | location: {
297 | type: Object,
298 | blackbox: true,
299 | optional: true,
300 | viewableBy: ['guests']
301 | },
302 |
303 | pricePerNight: {
304 | type: Number,
305 | optional: false,
306 | label: 'Price per night',
307 | viewableBy: ['guests'],
308 | insertableBy: ['members'],
309 | editableBy: ['members'],
310 | group: formGroups.price,
311 | control: 'number'
312 | },
313 |
314 | // GraphQL-only fields
315 |
316 | pageUrl: {
317 | type: String,
318 | optional: true,
319 | resolveAs: {
320 | type: 'String',
321 | resolver: (room, args, context) => {
322 | return `${Utils.getSiteUrl()}/room/${room._id}`;
323 | },
324 | }
325 | }
326 |
327 | };
328 |
329 | export default schema;
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | //.jshintrc
2 | {
3 | // JSHint Meteor Configuration File
4 | // Match the Meteor Style Guide
5 | //
6 | // By @raix with contributions from @aldeed and @awatson1978
7 | // Source https://github.com/raix/Meteor-jshintrc
8 | //
9 | // See http://jshint.com/docs/ for more details
10 |
11 | "maxerr" : 50, // {int} Maximum error before stopping
12 |
13 | // Enforcing
14 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
15 | "camelcase" : true, // true: Identifiers must be in camelCase
16 | // "curly" : true, // true: Require {} for every new block or scope
17 | "eqeqeq" : true, // true: Require triple equals (===) for comparison
18 | "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
19 | "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
20 | "indent" : 2, // {int} Number of spaces to use for indentation
21 | "latedef" : false, // true: Require variables/functions to be defined before being used
22 | "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
23 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
24 | "noempty" : true, // true: Prohibit use of empty blocks
25 | "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
26 | "plusplus" : false, // true: Prohibit use of `++` & `--`
27 | "quotmark" : false, // Quotation mark consistency:
28 | // false : do nothing (default)
29 | // true : ensure whatever is used is consistent
30 | // "single" : require single quotes
31 | // "double" : require double quotes
32 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
33 | "unused" : true, // true: Require all defined variables be used
34 | "strict" : false, // true: Requires all functions run in ES5 Strict Mode
35 | "trailing" : true, // true: Prohibit trailing whitespaces
36 | "maxparams" : false, // {int} Max number of formal params allowed per function
37 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions)
38 | "maxstatements" : false, // {int} Max number statements per function
39 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function
40 | "maxlen" : false, // {int} Max number of characters per line
41 |
42 | // Relaxing
43 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
44 | "boss" : false, // true: Tolerate assignments where comparisons would be expected
45 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
46 | "eqnull" : false, // true: Tolerate use of `== null`
47 | "es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
48 | "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
49 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
50 | // (ex: `for each`, multiple try/catch, function expression…)
51 | "evil" : false, // true: Tolerate use of `eval` and `new Function()`
52 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs
53 | "funcscope" : false, // true: Tolerate defining variables inside control statements"
54 | "globalstrict" : true, // true: Allow global "use strict" (also enables 'strict')
55 | "iterator" : false, // true: Tolerate using the `__iterator__` property
56 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
57 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings
58 | "laxcomma" : false, // true: Tolerate comma-first style coding
59 | "loopfunc" : false, // true: Tolerate functions being defined in loops
60 | "multistr" : false, // true: Tolerate multi-line strings
61 | "proto" : false, // true: Tolerate using the `__proto__` property
62 | "scripturl" : false, // true: Tolerate script-targeted URLs
63 | "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
64 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
65 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
66 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
67 | "validthis" : false, // true: Tolerate using this in a non-constructor function
68 |
69 | // Environments
70 | "browser" : true, // Web Browser (window, document, etc)
71 | "couch" : false, // CouchDB
72 | "devel" : true, // Development/debugging (alert, confirm, etc)
73 | "dojo" : false, // Dojo Toolkit
74 | "jasmine" : true, // Jasmine testing framework
75 | "jquery" : false, // jQuery
76 | "mootools" : false, // MooTools
77 | "node" : false, // Node.js
78 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
79 | "prototypejs" : false, // Prototype and Scriptaculous
80 | "rhino" : false, // Rhino
81 | "worker" : false, // Web Workers
82 | "wsh" : false, // Windows Scripting Host
83 | "yui" : false, // Yahoo User Interface
84 | //"meteor" : false, // Meteor.js
85 |
86 | // Legacy
87 | "nomen" : false, // true: Prohibit dangling `_` in variables
88 | "onevar" : false, // true: Allow only one `var` statement per function
89 | "passfail" : false, // true: Stop on first error
90 | "white" : false, // true: Check against strict whitespace and indentation rules
91 |
92 | // Custom globals, from http://docs.meteor.com, in the order they appear there
93 | "globals" : {
94 | "Meteor": false,
95 | "DDP": false,
96 | "Mongo": false, //Meteor.Collection renamed to Mongo.Collection
97 | "Session": false,
98 | "Accounts": false,
99 | "Template": false,
100 | "Blaze": false, //UI is being renamed Blaze
101 | "UI": false,
102 | "Match": false,
103 | "check": false,
104 | "Tracker": false, //Deps renamed to Tracker
105 | "Deps": false,
106 | "ReactiveVar": false,
107 | "EJSON": false,
108 | "HTTP": false,
109 | "Email": false,
110 | "Assets": false,
111 | "Handlebars": false, // https://github.com/meteor/meteor/wiki/Handlebars
112 | "Package": false,
113 |
114 | // Meteor internals
115 | "DDPServer": false,
116 | "global": false,
117 | "Log": false,
118 | "MongoInternals": false,
119 | "process": false,
120 | "WebApp": false,
121 | "WebAppInternals": false,
122 |
123 | // globals useful when creating Meteor packages
124 | "Npm": false,
125 | "Tinytest": false,
126 |
127 | // Meteor packages
128 | "$": false,
129 | "_": false,
130 | "__": false,
131 | "AccountsTemplates": false,
132 | "AutoForm": false,
133 | "Avatar": false,
134 | "Cookie": false,
135 | "FastRender": false,
136 | "Gravatar": false,
137 | "Herald": false,
138 | "Kadira": false,
139 | "moment": false,
140 | "Random": false,
141 | "RouteController": false,
142 | "Router": false,
143 | "SEO": false,
144 | "SimpleSchema": false,
145 | "SubsManager": false,
146 | "SyncedCron": false,
147 | "TAPi18n": false,
148 | "FlowRouter": false,
149 |
150 | // Telescope collections
151 | "Categories": true,
152 | "Comments": true,
153 | "Feeds": true,
154 | "Invites": true,
155 | "Migrations": true,
156 | "Posts": true,
157 | "Releases": true,
158 | "Searches": true,
159 | "Users": true,
160 |
161 | // Telescope objects
162 | "buildAndSendEmail": true,
163 | "buildEmailNotification": true,
164 | "buildEmailTemplate": true,
165 | "compareVersions": true,
166 | "coreSubscriptions": true,
167 | "daysPerPage": true,
168 | "deleteDummyContent": true,
169 | "Events": true,
170 | "fetchFeeds": true,
171 | "getCategoryUrl": true,
172 | "getEmailTemplate": true,
173 | "getPostCategories": true,
174 | "getTemplate": true,
175 | "getVotePower": true,
176 | "i18n": true,
177 | "InviteSchema": true,
178 | "logEvent": true,
179 | "marked": true,
180 | "Messages": true,
181 | "Pages": true,
182 | "sendEmail": true,
183 | "serveAPI": true,
184 | "Settings": true,
185 | "Telescope": true,
186 | "templates": true,
187 | "themeSettings": true
188 | },
189 | "esnext": true
190 | }
191 |
--------------------------------------------------------------------------------
/History.md:
--------------------------------------------------------------------------------
1 | ## v1.1.0 => vNEXT
2 |
3 | We now keep track of the version thanks to [`release`](https://github.com/zeit/release).
4 |
5 | To see the updates, check the [Nova's repo releases](https://github.com/TelescopeJS/Telescope/releases).
6 |
7 | ## v1.0.0
8 | - A lot of breaking changes: this is the Apollo/GraphQL version of Telescope Nova. [You can find the documentation here](http://nova-docs.telescopeapp.org/).
9 |
10 | ## v0.27.5
11 |
12 | - Nova is now powered by Meteor 1.4.2.3.
13 | - Newsletter settings + banner behavior fixed ([PR #1513](https://github.com/TelescopeJS/Telescope/pull/1513), thanks [@bengott](https://github.com/bengott)). **⚠️ If you are updating from a previous version of Nova/Legacy, [make sure to run migration script in your app on a server file!](https://github.com/bengott/Telescope/blob/e714aab27b323aee5ddbb97a5ece01a3cdc0e76f/packages/_nova-migrations/lib/server/migrations.js#L746-L782)**
14 | - Fix ESLint issues: revert comment in deep function, support for JSX files, clean up code ([PR #1511](https://github.com/TelescopeJS/Telescope/pull/1511), [PR #1512](https://github.com/TelescopeJS/Telescope/pull/1512), [PR #1515](https://github.com/TelescopeJS/Telescope/pull/1515), thanks [@comus](https://github.com/comus)).
15 | - Remove legacy packages not used anymore `telescope:migrations` & `telescope:invites`.
16 | - Packages with **_** are unmaintained & necessary Meteor packages for Nova to run. We have added small patches to them: `meteorhacks:inject-data` & `react-router:react-router-ssr`.
17 |
18 | ## v0.27.4
19 |
20 | - Nova is now powered by Meteor 1.4.2.1, which provides among other cool features super fast build time! Some NPM dependencies changed: **be sure to run `npm install` again!**
21 | - Fix typo in class name `posts-list-header-categories` ([PR #1487](https://github.com/TelescopeJS/Telescope/pull/1487), thanks [@seanjsong](https://github.com/seanjsong)).
22 | - Make `document` property available to all form components, but don't pass it down to standard input controls to avoid error.
23 | - Do not try to init legacy `Settings` collection client-side: this was an annoying warning that you may have got telling something about a forbidden insert.
24 | - Add reset password components and route ([PR #1491](https://github.com/TelescopeJS/Telescope/pull/1491), thanks [@malively](https://github.com/malively)).
25 | - Add internationalization messages for "no more posts", "no results" and "load more days" ([PR #1499](https://github.com/TelescopeJS/Telescope/pull/1499), thanks [@qge](https://github.com/qge)).
26 | - No more duplicate slugs if a user signs up with an external service (Facebook, Twitter, ..) and another user signs up with a username being the same as the other user (modification on `Telescope.utils.getUnsudedSlug` to handle edge case on `Users` collection).
27 | - Somebody can remove their account themselves again: `users.remove` fixed at the level of the permissions and related callbacks.
28 | - Server-side rendering / data-injection is fixed thanks to a `meteorhacks:inject-data` fork added locally in the packages (`nova-inject-data` folder, [see here for more info](https://github.com/TelescopeJS/Telescope/commit/f988686653b21896c3f5d321f30c34c1b5778628#diff-8f4ee0b18f5c4673b79684ee3c7d2430))
29 | - Add simplified chinese (`zh-CN`) translation package to the README ([PR #1503](https://github.com/TelescopeJS/Telescope/pull/1503), thanks [@qge](https://github.com/qge)).
30 | - Only show comment reply button for logged in users ([PR #1504](https://github.com/TelescopeJS/Telescope/pull/1504), thanks [@qge](https://github.com/qge)).
31 | - Fix React `setState` race condition on `NovaForm` autofilled values ([PR #1507](https://github.com/TelescopeJS/Telescope/pull/1507), thanks [@sherryxiao1988](https://github.com/sherryxiao1988)).
32 | - Fix ESLint config: you can lint your project with `npm run lint`! It is based on `eslint:recommended` + `meteor` extends ([PR #1474](https://github.com/TelescopeJS/Telescope/pull/1474), thanks [@moimikey](https://github.com/moimikey)).
33 |
34 | ## v0.27.3
35 |
36 | - Explain with more details how to deploy with Meteor Up (PR [#1456](https://github.com/TelescopeJS/Telescope/pull/1456), thanks [@asmita005](https://github.com/asmita005)!).
37 | - Add slug to `newPendingPost` notifications, fixes [#1254](https://github.com/TelescopeJS/Telescope/issues/1254).
38 | - Ensure slug unicity on user's slug as done as category's slug (use of `Telescope.utils.getUnusedSlug`), fixes [#1213](https://github.com/TelescopeJS/Telescope/issues/1213).
39 | - Remove some dead code from Telescope Legacy.
40 | - Use of Comment's `getPageUrl` helper in `nova:rss`.
41 | - Prefer `Users` namespace to `Meteor.users` in active packages.
42 | - If you used the property `autoform` on your custom fields, it's now entitled `form`. This was an old reference to [AutoForm](https://github.com/aldeed/meteor-autoform) used by Telescope Legacy. We will give you a console warning if you still use it to advice you to change it.
43 | - Fix errors on `nova:forms`: callbacks from components wrapping a `NovaForm` (ex: `ModalTrigger`) are not fired anymore when it has already been unmounted.
44 | - Fix errors when logging out from the "profile check modal" (`UsersProfileCheck`).
45 | - Prevent errors when creating/editing a category with custom fields (load order of smart methods with extended schema).
46 | - The callback on `nova:subscribe` related to categories has been updated to prevent a user from receiving multiple emails if he/she is subscribed to multiple categories (PR [#1466](https://github.com/TelescopeJS/Telescope/pull/1466), thanks [@chptung](https://github.com/chptung)).
47 | - You can now submit a post/comments (or any `NovaForm` comp) with CMD + Enter / Ctrl + Enter shortcuts (PR [#1472](https://github.com/TelescopeJS/Telescope/pull/1472), thanks [@aszx87410](https://github.com/aszx87410)).
48 | - You can run Telescope Nova inside Docker without deploying (see [this awesome guide](http://spartatek.se/meteor_blog/docker/2016/01/12/running-telescope.html)), fixes [#1477](https://github.com/TelescopeJS/Telescope/issues/1477)
49 | - Add `flex-wrap: wrap;` to posts-categories class for better styling if a user creates a post with too many categories (PR [#1469](https://github.com/TelescopeJS/Telescope/pull/1469), thanks [@chptung](https://github.com/chptung)).
50 |
51 | **Changes that may break some parts of your app:**
52 | - Some callbacks have been renamed for consistency purposes: `postsParameters` becomes `posts.parameters`, `profileCompletedAsync` becomes `users.profileCompleted.async`, `profileCompletedChecks` becomes `users.profileCompleted.sync`, `onCreateUserAsync` becomes `users.new.async`, `onCreateUser` becomes `users.new.sync`, `UsersEdit` becomes `users.edit.sync`, `UsersEditAsync` becomes `users.edit.async`.
53 | - The use of `react-bootstrap@0.30.3` is now forced in `package.json`: the latest versions break the dropdown at the moment (see [#1463](https://github.com/TelescopeJS/Telescope/issues/1463)). You should re-run `npm install` if you update from a previous version.
54 | - The `currentUser` props has been removed, the current user is explicitly passed through the context as a matter of consistency across the app. If one of your custom components extending one of `nova:base-components` used `currentUser` as a props, you should update it to use it via the context and add the corresponding contextTypes. See commit [b04cb52](https://github.com/TelescopeJS/Telescope/commit/b04cb5247027fc431f7aa1704ef823ac8ce5fdd1).
55 |
56 | ## v0.27.2
57 |
58 | - Move `updateCurrentValue` function from `propTypes` to `contextTypes` in the datetime picker`DateTime` (`nova:forms`) ([#1449](https://github.com/TelescopeJS/Telescope/issues/1449)).
59 | - Check duplicate links on post's edit [(#247](https://github.com/TelescopeJS/Telescope/issues/247)).
60 | - Cloudinary images from `nova:cloudinary` are now served over HTTPS ([#1224](https://github.com/TelescopeJS/Telescope/issues/1224)).
61 | - Add year and name to licence ([#1117](https://github.com/TelescopeJS/Telescope/issues/1117)).
62 | - Clean Legacy's issues & PRs. Be ready for the [Hacktoberfest](https://hacktoberfest.digitalocean.com/)!! 🍻
63 |
64 | ## v0.27.1
65 |
66 | - Nova uses now React 15.3.x with associated Node modules, besides it prevents unknown prop warnings ([React docs](https://facebook.github.io/react/warnings/unknown-prop.html)). We still depends on `react-meteor-data` and mixins to load data (thanks @MHerszak for careful watch!), we may move soon to Apollo (contributions welcomed on `[apollo](https://github.com/TelescopeJS/Telescope/tree/apollo)` branch).
67 | - README updated. On deployment recommandations: you should go with Mup 1.0.3 ([repo](https://github.com/kadirahq/meteor-up)), MupX is not compatible with Meteor 1.4 ; on 3rd-party packages section, you can now upload images to a CDN ([package](https://github.com/xavcz/nova-forms-upload)).
68 | - The 404 Not Found route has been brought back to `nova:base-routes`, you can customize its shape by editing `Error404.jsx` (`nova:base-components`).
69 | - Added support for a custom CSS class for `SubscribeTo` component.
70 | - No more global variable in `nova:api` (the last one? \o/).
71 | - Fix a version problem with `fourseven:scss`, now running on 3.9.0.
72 | - Remove unnecessary NPM dependency on `load-script` (thanks [@MHerszak](https://github.com/mherszak)!).
73 | - You can now run Nova in Brazilian Portuguese by [adding this package](https://github.com/lukasag/nova-i18n-pt-br) (thanks [@lukasag](https://github.com/lukasag)!).
74 | - Added support for a `defaultValue` property in `nova:forms`. You can define it in your custom fields, it will be added if no value nor prefilled value is defined (thanks [@beeva-franciscocalle](https://github.com/beeva-franciscocalle)!).
75 | - Fixed edge bug when users don't have an `username`, use `displayName` instead (thanks [@jeffreywyman](https://github.com/jeffreywyman)!).
76 |
77 | ## v0.27.0
78 |
79 | - Remove Telescope global variable.
80 | - Update to Meteor 1.4.
81 | - A user can now subscribe to any collection with `nova:subscribe` package ([docs](https://github.com/TelescopeJS/Telescope/tree/master/packages/nova-subscribe)) and a reusable `SubscribeTo` component (thanks [@schabluk](https://github.com/schabluk)!).
82 |
83 | *The rest of the modifications are not yet documented, you can [browse the commits history from there](https://github.com/TelescopeJS/Telescope/commits/2b34713c0b6dbf094668f8a87d007443a1e2c580).*
84 |
85 | ## v0.26.5
86 |
87 | - Creation of a permissions (groups) API for the users.
88 | - The routes are now more easy to customize, [see docs](https://github.com/TelescopeJS/Telescope/tree/master#routes).
89 |
90 | *The rest of the modifications are not yet documented, you can [browse the commits history from there](https://github.com/TelescopeJS/Telescope/commits/cfc52b1158f3dd9cfc98ef5081f558112dc3c3cc).*
91 |
92 | ## v0.26.4
93 |
94 | - Collections are not globals anymore, you need to import them in order to use them.
95 | - NovaForm has been improved with a placeholder options for text fields and you can enhance any field with components placed before and after it.
96 |
97 | *The rest of the modifications are not yet documented, you can [browse the commits history from there](https://github.com/TelescopeJS/Telescope/commits/4f61940b07c48c6b3c7f13a47002c0199652a346).*
98 |
99 | ## v0.26.3
100 |
101 | - Switch from FlowRouter to React-Router v3.
102 |
103 | *The rest of the modifications are not yet documented, you can [browse the commits history from there](https://github.com/TelescopeJS/Telescope/commits/7b8624f709b6130fa8f93a141775491dc2455bbf).*
104 |
105 | ## v0.26.2
106 |
107 | - Made component names more consistent; Collection names (“Posts”, “Comments”, etc.) are **always plural** in component names.
108 | - Routes now live in their own package (`nova:base-routes`).
109 | - The search now searches in the `excerpt` field, not `body`, because `body` is not published to the client (and searches would give different results on client and server).
110 | - Removed option to manually set a post's author.
111 | - The Embedly thumbnail feature now includes a "clear thumbnail" link to remove it and an option to enter a URL manually.
112 | - There is now an autofill tags component you can optionally include and use with `meteor add nova:forms-tags` (see Embedly package custom fields for how to use custom components in forms).
113 | - You can now see a post's ID and stats in the post edit form if you're an admin.
114 | - Fixed bug (I hope?) where daily view would become messed up when client and server were on different timezones.
115 | - Now showing a user's posts on their profile page.
116 | - Added soft delete for comments (thanks [@justintime4tea](https://github.com/justintime4tea)!).
117 | - Fixed posts notifications bugs.
118 | - Got rid of a lot of Meteor packages in favor of NPM equivalents.
119 |
120 | ## v0.25.7
121 |
122 | First Nova version.
123 |
--------------------------------------------------------------------------------