amazon-chime-sdk-rails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +87 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +47 -0
  5. data/.yardopts +6 -0
  6. data/CHANGELOG.md +3 -0
  7. data/Gemfile +25 -0
  8. data/LICENSE +21 -0
  9. data/README.md +1109 -0
  10. data/Rakefile +20 -0
  11. data/amazon-chime-sdk-rails.gemspec +29 -0
  12. data/gemfiles/Gemfile.rails-5.0 +25 -0
  13. data/gemfiles/Gemfile.rails-5.1 +25 -0
  14. data/gemfiles/Gemfile.rails-5.2 +25 -0
  15. data/gemfiles/Gemfile.rails-6.0 +25 -0
  16. data/lib/amazon-chime-sdk-rails.rb +37 -0
  17. data/lib/chime_sdk/config.rb +90 -0
  18. data/lib/chime_sdk/controller/attendees.rb +128 -0
  19. data/lib/chime_sdk/controller/common.rb +202 -0
  20. data/lib/chime_sdk/controller/meetings.rb +192 -0
  21. data/lib/chime_sdk/meeting_coordinator.rb +184 -0
  22. data/lib/chime_sdk/version.rb +4 -0
  23. data/lib/generators/chime_sdk/controllers_generator.rb +104 -0
  24. data/lib/generators/chime_sdk/install_generator.rb +27 -0
  25. data/lib/generators/chime_sdk/js_generator.rb +67 -0
  26. data/lib/generators/chime_sdk/views_generator.rb +43 -0
  27. data/lib/generators/templates/chime_sdk.rb +28 -0
  28. data/lib/generators/templates/controllers/README +35 -0
  29. data/lib/generators/templates/controllers/meeting_attendees_controller.rb +106 -0
  30. data/lib/generators/templates/controllers/meetings_controller.rb +146 -0
  31. data/lib/generators/templates/views/meetings/index.html.erb +27 -0
  32. data/lib/generators/templates/views/meetings/show.html.erb +136 -0
  33. data/spec/factories/rooms.rb +5 -0
  34. data/spec/factories/users.rb +8 -0
  35. data/spec/generators/controllers_generator_spec.rb +113 -0
  36. data/spec/generators/install_generator_spec.rb +24 -0
  37. data/spec/generators/js_generator_spec.rb +26 -0
  38. data/spec/generators/views_generator_spec.rb +46 -0
  39. data/spec/rails_app/Rakefile +15 -0
  40. data/spec/rails_app/app/assets/config/manifest.js +2 -0
  41. data/spec/rails_app/app/assets/images/.keep +0 -0
  42. data/spec/rails_app/app/assets/javascripts/.keep +0 -0
  43. data/spec/rails_app/app/assets/stylesheets/application.css +15 -0
  44. data/spec/rails_app/app/assets/stylesheets/scaffolds.scss +65 -0
  45. data/spec/rails_app/app/controllers/api/meeting_attendees_controller.rb +3 -0
  46. data/spec/rails_app/app/controllers/api/meetings_controller.rb +3 -0
  47. data/spec/rails_app/app/controllers/api/rooms_controller.rb +3 -0
  48. data/spec/rails_app/app/controllers/application_controller.rb +11 -0
  49. data/spec/rails_app/app/controllers/entries_controller.rb +47 -0
  50. data/spec/rails_app/app/controllers/meeting_attendees_controller.rb +121 -0
  51. data/spec/rails_app/app/controllers/meetings_controller.rb +162 -0
  52. data/spec/rails_app/app/controllers/rooms_controller.rb +76 -0
  53. data/spec/rails_app/app/controllers/spa_controller.rb +6 -0
  54. data/spec/rails_app/app/javascript/App.vue +50 -0
  55. data/spec/rails_app/app/javascript/channels/consumer.js +6 -0
  56. data/spec/rails_app/app/javascript/channels/index.js +5 -0
  57. data/spec/rails_app/app/javascript/components/DeviseTokenAuth.vue +84 -0
  58. data/spec/rails_app/app/javascript/components/meetings/Index.vue +100 -0
  59. data/spec/rails_app/app/javascript/components/meetings/Meeting.vue +178 -0
  60. data/spec/rails_app/app/javascript/components/rooms/Index.vue +53 -0
  61. data/spec/rails_app/app/javascript/components/rooms/Show.vue +91 -0
  62. data/spec/rails_app/app/javascript/packs/application.js +17 -0
  63. data/spec/rails_app/app/javascript/packs/spa.js +14 -0
  64. data/spec/rails_app/app/javascript/router/index.js +74 -0
  65. data/spec/rails_app/app/javascript/store/index.js +37 -0
  66. data/spec/rails_app/app/models/application_record.rb +3 -0
  67. data/spec/rails_app/app/models/entry.rb +5 -0
  68. data/spec/rails_app/app/models/room.rb +12 -0
  69. data/spec/rails_app/app/models/user.rb +6 -0
  70. data/spec/rails_app/app/views/devise/registrations/new.html.erb +34 -0
  71. data/spec/rails_app/app/views/layouts/_header.html.erb +20 -0
  72. data/spec/rails_app/app/views/layouts/application.html.erb +18 -0
  73. data/spec/rails_app/app/views/meetings/index.html.erb +28 -0
  74. data/spec/rails_app/app/views/meetings/show.html.erb +136 -0
  75. data/spec/rails_app/app/views/rooms/_form.html.erb +22 -0
  76. data/spec/rails_app/app/views/rooms/_room.json.jbuilder +7 -0
  77. data/spec/rails_app/app/views/rooms/edit.html.erb +6 -0
  78. data/spec/rails_app/app/views/rooms/index.html.erb +27 -0
  79. data/spec/rails_app/app/views/rooms/index.json.jbuilder +1 -0
  80. data/spec/rails_app/app/views/rooms/new.html.erb +5 -0
  81. data/spec/rails_app/app/views/rooms/show.html.erb +42 -0
  82. data/spec/rails_app/app/views/rooms/show.json.jbuilder +1 -0
  83. data/spec/rails_app/app/views/spa/index.html.erb +1 -0
  84. data/spec/rails_app/babel.config.js +72 -0
  85. data/spec/rails_app/bin/bundle +114 -0
  86. data/spec/rails_app/bin/rails +9 -0
  87. data/spec/rails_app/bin/rake +9 -0
  88. data/spec/rails_app/bin/setup +36 -0
  89. data/spec/rails_app/bin/spring +17 -0
  90. data/spec/rails_app/bin/webpack +18 -0
  91. data/spec/rails_app/bin/webpack-dev-server +18 -0
  92. data/spec/rails_app/bin/yarn +11 -0
  93. data/spec/rails_app/config.ru +5 -0
  94. data/spec/rails_app/config/application.rb +21 -0
  95. data/spec/rails_app/config/boot.rb +4 -0
  96. data/spec/rails_app/config/cable.yml +10 -0
  97. data/spec/rails_app/config/credentials.yml.enc +1 -0
  98. data/spec/rails_app/config/database.yml +25 -0
  99. data/spec/rails_app/config/environment.rb +14 -0
  100. data/spec/rails_app/config/environments/development.rb +62 -0
  101. data/spec/rails_app/config/environments/production.rb +112 -0
  102. data/spec/rails_app/config/environments/test.rb +49 -0
  103. data/spec/rails_app/config/initializers/application_controller_renderer.rb +8 -0
  104. data/spec/rails_app/config/initializers/assets.rb +15 -0
  105. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  106. data/spec/rails_app/config/initializers/chime_sdk.rb +28 -0
  107. data/spec/rails_app/config/initializers/content_security_policy.rb +30 -0
  108. data/spec/rails_app/config/initializers/cookies_serializer.rb +5 -0
  109. data/spec/rails_app/config/initializers/devise.rb +311 -0
  110. data/spec/rails_app/config/initializers/devise_token_auth.rb +60 -0
  111. data/spec/rails_app/config/initializers/filter_parameter_logging.rb +4 -0
  112. data/spec/rails_app/config/initializers/inflections.rb +16 -0
  113. data/spec/rails_app/config/initializers/mime_types.rb +4 -0
  114. data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
  115. data/spec/rails_app/config/locales/devise.en.yml +65 -0
  116. data/spec/rails_app/config/locales/en.yml +33 -0
  117. data/spec/rails_app/config/puma.rb +38 -0
  118. data/spec/rails_app/config/routes.rb +24 -0
  119. data/spec/rails_app/config/secrets.yml +8 -0
  120. data/spec/rails_app/config/spring.rb +6 -0
  121. data/spec/rails_app/config/storage.yml +34 -0
  122. data/spec/rails_app/config/webpack/development.js +5 -0
  123. data/spec/rails_app/config/webpack/environment.js +7 -0
  124. data/spec/rails_app/config/webpack/loaders/vue.js +6 -0
  125. data/spec/rails_app/config/webpack/production.js +5 -0
  126. data/spec/rails_app/config/webpack/test.js +5 -0
  127. data/spec/rails_app/config/webpacker.yml +97 -0
  128. data/spec/rails_app/db/migrate/20200912140231_devise_create_users.rb +45 -0
  129. data/spec/rails_app/db/migrate/20200912140352_add_tokens_to_users.rb +12 -0
  130. data/spec/rails_app/db/migrate/20200912140657_create_rooms.rb +9 -0
  131. data/spec/rails_app/db/migrate/20200912140749_create_entries.rb +10 -0
  132. data/spec/rails_app/db/schema.rb +49 -0
  133. data/spec/rails_app/db/seeds.rb +41 -0
  134. data/spec/rails_app/log/.keep +0 -0
  135. data/spec/rails_app/package.json +23 -0
  136. data/spec/rails_app/postcss.config.js +12 -0
  137. data/spec/rails_app/public/404.html +67 -0
  138. data/spec/rails_app/public/422.html +67 -0
  139. data/spec/rails_app/public/500.html +66 -0
  140. data/spec/rails_app/public/favicon.ico +0 -0
  141. data/spec/rails_app/public/robots.txt +1 -0
  142. data/spec/rails_app/tmp/.keep +0 -0
  143. data/spec/requests/atendees_spec.rb +182 -0
  144. data/spec/requests/meetings_spec.rb +433 -0
  145. data/spec/spec_helper.rb +35 -0
  146. metadata +400 -0
@@ -0,0 +1,6 @@
1
+ // Action Cable provides the framework to deal with WebSockets in Rails.
2
+ // You can generate new channels where WebSocket features live using the `rails generate channel` command.
3
+
4
+ import { createConsumer } from "@rails/actioncable"
5
+
6
+ export default createConsumer()
@@ -0,0 +1,5 @@
1
+ // Load all the channels within this directory and all subdirectories.
2
+ // Channel files must be named *_channel.js.
3
+
4
+ const channels = require.context('.', true, /_channel\.js$/)
5
+ channels.keys().forEach(channels)
@@ -0,0 +1,84 @@
1
+ <template>
2
+ <div id="login">
3
+ <h2>Log in</h2>
4
+ <form class="new_user" @submit.prevent="login">
5
+ <div class="field">
6
+ <label for="user_email">Email</label><br />
7
+ <input v-model="loginParams.email" autofocus="autofocus" autocomplete="email" type="email" value="" name="user[email]" id="user_email" />
8
+ </div>
9
+ <div class="field">
10
+ <label for="user_password">Password</label><br />
11
+ <input v-model="loginParams.password" autocomplete="current-password" type="password" name="user[password]" id="user_password" />
12
+ </div>
13
+ <div class="actions">
14
+ <input type="submit" name="commit" value="Log in" data-disable-with="Log in" />
15
+ </div>
16
+ </form>
17
+
18
+ <a href="/users/sign_up">(Sign up)</a><br />
19
+ </div>
20
+ </template>
21
+
22
+ <script>
23
+ import axios from 'axios'
24
+
25
+ export default {
26
+ name: 'DeviseTokenAuth',
27
+ props: {
28
+ isLogout: {
29
+ type: Boolean,
30
+ default: false
31
+ }
32
+ },
33
+ data () {
34
+ return {
35
+ loginParams: {
36
+ email: "",
37
+ password: ""
38
+ }
39
+ }
40
+ },
41
+ mounted () {
42
+ if (this.isLogout) {
43
+ this.logout();
44
+ }
45
+ },
46
+ methods: {
47
+ login () {
48
+ axios
49
+ .post('/auth/sign_in', { email: this.loginParams.email, password: this.loginParams.password })
50
+ .then(response => {
51
+ if (response.status == 200) {
52
+ let authHeaders = {};
53
+ for (let authHeader of ['access-token', 'client', 'uid']) {
54
+ authHeaders[authHeader] = response.headers[authHeader];
55
+ axios.defaults.headers.common[authHeader] = authHeaders[authHeader];
56
+ }
57
+ this.$store.commit('signIn', { user: response.data.data, authHeaders: authHeaders });
58
+ if (this.$route.query.redirect) {
59
+ this.$router.push(this.$route.query.redirect);
60
+ } else {
61
+ this.$router.push('/');
62
+ }
63
+ }
64
+ })
65
+ .catch (error => {
66
+ console.log("Authentication failed");
67
+ if (error.response.status == 401) {
68
+ this.$router.go({path: this.$router.currentRoute.path});
69
+ }
70
+ })
71
+ },
72
+ logout () {
73
+ for (var authHeader of Object.keys(this.$store.getters.authHeaders)) {
74
+ delete axios.defaults.headers.common[authHeader];
75
+ }
76
+ this.$store.commit('signOut');
77
+ this.$router.push('/');
78
+ }
79
+ }
80
+ }
81
+ </script>
82
+
83
+ <style scoped>
84
+ </style>
@@ -0,0 +1,100 @@
1
+ <template>
2
+ <div>
3
+ <p id="notice">{{ notice }}</p>
4
+
5
+ <h1>Meetings by Amazon Chime SDK</h1>
6
+
7
+ <table>
8
+ <thead>
9
+ <tr>
10
+ <th>Meeting ID</th>
11
+ <th>External Meeting ID</th>
12
+ <th>Media Region</th>
13
+ <th colspan="1"></th>
14
+ </tr>
15
+ </thead>
16
+
17
+ <tbody>
18
+ <tr v-for="meeting in meetings" :key="`${meeting.Meeting.MeetingId}`">
19
+ <td><router-link v-bind:to="{ name: 'Meeting', params: { room_id: room_id, meeting_id: meeting.Meeting.MeetingId } }">{{ meeting.Meeting.MeetingId }}</router-link></td>
20
+ <td>{{ meeting.Meeting.ExternalMeetingId }}</td>
21
+ <td>{{ meeting.Meeting.MediaRegion }}</td>
22
+ <td><a href="#" v-on:click="delete_meeting(meeting.Meeting.MeetingId, 'Are you sure?')" data-remote="true">Destroy</a></td>
23
+ </tr>
24
+ </tbody>
25
+ </table>
26
+
27
+ <p><a href="#" v-on:click="create_meeting(room_id)" data-remote="true">Create Meeting</a></p>
28
+ <p><router-link v-bind:to="{ name: 'Room', params: { room_id: room_id } }">Back to Room</router-link></p>
29
+ </div>
30
+ </template>
31
+
32
+ <script>
33
+ import axios from 'axios'
34
+
35
+ export default {
36
+ name: 'Meetings',
37
+ props: {
38
+ room_id: {
39
+ required: true
40
+ },
41
+ message: String
42
+ },
43
+ data () {
44
+ return {
45
+ meetings: [],
46
+ notice: this.message
47
+ }
48
+ },
49
+ mounted () {
50
+ axios
51
+ .get(`/rooms/${this.room_id}/meetings`)
52
+ .then(response => {
53
+ this.meetings = response.data.meetings;
54
+ })
55
+ .catch(error => {
56
+ if (error.response.status == 403) {
57
+ this.$router.push({ name: 'Room', params: { room_id: this.room_id, message: error.response.data.notice } });
58
+ }
59
+ this.notice = error;
60
+ })
61
+ },
62
+ methods: {
63
+ create_meeting (room_id) {
64
+ axios
65
+ .post(`/rooms/${this.room_id}/meetings`)
66
+ .then(response => {
67
+ if (response.status == 201) {
68
+ let meeting_id = response.data.Meeting.MeetingId;
69
+ this.$router.push({ name: 'Meeting', params: { room_id: this.room_id, meeting_id: meeting_id, message: `Meeting <${meeting_id}> was successfully created.` } });
70
+ }
71
+ })
72
+ .catch (error => {
73
+ if (error.response.status == 403) {
74
+ this.notice = error.response.data.notice;
75
+ } else {
76
+ this.notice = error;
77
+ }
78
+ })
79
+ },
80
+ delete_meeting(meeting_id, confirmation) {
81
+ if (confirm(confirmation)) {
82
+ axios
83
+ .delete(`/rooms/${this.room_id}/meetings/${meeting_id}`)
84
+ .then(response => {
85
+ if (response.status == 204) {
86
+ this.notice = `Meeting <${meeting_id}> was successfully destroyed.`;
87
+ }
88
+ })
89
+ .catch (error => {
90
+ console.log(error.response);
91
+ this.notice = error;
92
+ })
93
+ }
94
+ }
95
+ }
96
+ }
97
+ </script>
98
+
99
+ <style scoped>
100
+ </style>
@@ -0,0 +1,178 @@
1
+ <template>
2
+ <div>
3
+ <p id="notice">{{ notice }}</p>
4
+
5
+ <h1>Meeting by Amazon Chime SDK</h1>
6
+
7
+ <p><router-link v-bind:to="{ name: 'Meetings', params: { room_id: room_id } }">Show Meetings</router-link></p>
8
+
9
+ <div v-if="meeting">
10
+ <strong>Meeting</strong>
11
+ <p>
12
+ Meeting ID : <router-link v-bind:to="{ name: 'Meeting', params: { room_id: room_id, meeting_id: meeting.Meeting.MeetingId } }">{{ meeting.Meeting.MeetingId }}</router-link><br>
13
+ External Meeting ID : {{ meeting.Meeting.ExternalMeetingId }}<br>
14
+ Media Region : {{ meeting.Meeting.MediaRegion }}
15
+ </p>
16
+ </div>
17
+
18
+ <div v-if="attendee">
19
+ <strong>Attendee</strong>
20
+ <p>
21
+ Attendee ID : <a v-bind:href="`/rooms/${room_id}/meetings/${meeting.Meeting.MeetingId}/attendees/${attendee.Attendee.AttendeeId}`">{{ attendee.Attendee.AttendeeId }}</a><br>
22
+ External User ID : {{ attendee.Attendee.ExternalUserId }}<br>
23
+ Application Attendee Name : {{ attendee.Attendee.ApplicationMetadata.User.name }}
24
+ </p>
25
+
26
+ <div id="status">
27
+ <strong>Status</strong>
28
+ <div v-if="meetingOnline">
29
+ <div id="meeting-status">
30
+ <p>Meeting : Online <input type="button" value="Leave a meeting" v-on:click="leave()"></p>
31
+ <div id="audio-status">
32
+ <div v-if="meetingMuted">
33
+ <p>Audio : Muted <input type="button" value="Unmute" v-on:click="unmute()"></p>
34
+ </div>
35
+ <div v-else>
36
+ <p>Audio : Active <input type="button" value="Mute" v-on:click="mute()"></p>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ <div id="attendee-status">
41
+ <strong>Present Attendees</strong>
42
+ <ul>
43
+ <li v-for="attendeeName in presentAttendees" :key="attendeeName">{{ attendeeName }}</li>
44
+ </ul>
45
+ </div>
46
+ </div>
47
+ <div v-else>
48
+ <div id="meeting-status">
49
+ <p>Meeting : Offline <input type="button" value="Join a meeting" v-on:click="join()"></p>
50
+ </div>
51
+ </div>
52
+ </div>
53
+
54
+ <audio id="audio"></audio>
55
+ </div>
56
+ </div>
57
+ </template>
58
+
59
+ <script>
60
+ import axios from 'axios'
61
+ import {
62
+ ConsoleLogger,
63
+ DefaultDeviceController,
64
+ DefaultMeetingSession,
65
+ LogLevel,
66
+ MeetingSessionConfiguration
67
+ } from 'amazon-chime-sdk-js'
68
+
69
+ export default {
70
+ name: 'Meeting',
71
+ props: {
72
+ room_id: {
73
+ required: true
74
+ },
75
+ meeting_id: {
76
+ required: true
77
+ },
78
+ message: String
79
+ },
80
+ data () {
81
+ return {
82
+ meeting: null,
83
+ attendee: null,
84
+ notice: this.message,
85
+ meetingOnline: false,
86
+ meetingMuted: false,
87
+ presentAttendees: []
88
+ }
89
+ },
90
+ mounted () {
91
+ axios
92
+ .get(`/rooms/${this.room_id}/meetings/${this.meeting_id}`)
93
+ .then(response => {
94
+ this.meeting = { Meeting: response.data.Meeting };
95
+ this.attendee = { Attendee: response.data.Attendee };
96
+
97
+ const logger = new ConsoleLogger('ChimeMeetingLogs', LogLevel.INFO);
98
+ const deviceController = new DefaultDeviceController(logger);
99
+ const configuration = new MeetingSessionConfiguration(this.meeting, this.attendee);
100
+ this.meetingSession = new DefaultMeetingSession(configuration, logger, deviceController);
101
+ })
102
+ .catch(error => {
103
+ if (error.response.status == 404) {
104
+ this.$router.push({ name: 'Meetings', params: { room_id: this.room_id, message: `${error.response.data.error.message}: ${error.response.data.error.type}` } });
105
+ } else {
106
+ this.notice = error;
107
+ }
108
+ })
109
+ },
110
+ methods: {
111
+ // Customize this function to extract attendee name to show
112
+ showApplicationUserName(attendee) {
113
+ // return attendee.Attendee.AttendeeId;
114
+ return `${attendee.Attendee.ApplicationMetadata.User.name} (${attendee.Attendee.AttendeeId})`;
115
+ },
116
+ async join() {
117
+ try {
118
+ const audioInputDevices = await this.meetingSession.audioVideo.listAudioInputDevices();
119
+ const audioOutputDevices = await this.meetingSession.audioVideo.listAudioOutputDevices();
120
+ await this.meetingSession.audioVideo.chooseAudioInputDevice(audioInputDevices[0].deviceId);
121
+ await this.meetingSession.audioVideo.chooseAudioOutputDevice(audioOutputDevices[0].deviceId);
122
+ } catch (error) {
123
+ // handle error - unable to acquire audio device perhaps due to permissions blocking
124
+ console.log(error);
125
+ }
126
+ const audioOutputElement = document.getElementById('audio');
127
+ this.meetingSession.audioVideo.bindAudioElement(audioOutputElement);
128
+ this.meetingSession.audioVideo.start();
129
+ this.meetingOnline = true;
130
+ this.parepareAttendeeStatus();
131
+ },
132
+ leave() {
133
+ this.meetingSession.audioVideo.stop();
134
+ this.meetingOnline = false;
135
+ this.meetingMuted = false;
136
+ this.presentAttendees = [];
137
+ },
138
+ mute() {
139
+ this.meetingSession.audioVideo.realtimeMuteLocalAudio();
140
+ this.meetingMuted = true;
141
+ },
142
+ unmute() {
143
+ const unmuted = this.meetingSession.audioVideo.realtimeUnmuteLocalAudio();
144
+ if (unmuted) {
145
+ this.meetingMuted = false;
146
+ } else {
147
+ console.log('You cannot unmute yourself');
148
+ }
149
+ },
150
+ parepareAttendeeStatus() {
151
+ this.presentAttendeeMap = {};
152
+ const callback = (presentAttendeeId, present) => {
153
+ if (present) {
154
+ axios
155
+ .get(`/rooms/${this.room_id}/meetings/${this.meeting_id}/attendees/${presentAttendeeId}`)
156
+ .then(response => {
157
+ this.presentAttendeeMap[presentAttendeeId] = response.data;
158
+ this.updateAttendeeStatus(this.presentAttendeeMap);
159
+ })
160
+ .catch(error => {
161
+ console.log(error);
162
+ });
163
+ } else {
164
+ delete this.presentAttendeeMap[presentAttendeeId]
165
+ this.updateAttendeeStatus(this.presentAttendeeMap);
166
+ }
167
+ };
168
+ this.meetingSession.audioVideo.realtimeSubscribeToAttendeeIdPresence(callback);
169
+ },
170
+ updateAttendeeStatus(presentAttendeeMap) {
171
+ this.presentAttendees = Object.values(presentAttendeeMap).map(attendee => this.showApplicationUserName(attendee));
172
+ }
173
+ }
174
+ }
175
+ </script>
176
+
177
+ <style scoped>
178
+ </style>
@@ -0,0 +1,53 @@
1
+ <template>
2
+ <div>
3
+ <p id="notice">{{ notice }}</p>
4
+
5
+ <h1>Rooms</h1>
6
+
7
+ <table>
8
+ <thead>
9
+ <tr>
10
+ <th>Name</th>
11
+ <th colspan="3"></th>
12
+ </tr>
13
+ </thead>
14
+
15
+ <tbody>
16
+ <tr v-for="room in rooms" :key="`${room.id}`">
17
+ <td>{{ room.name }}</td>
18
+ <td><router-link v-bind:to="{ name: 'Room', params: { room_id: room.id } }">Show</router-link></td>
19
+ <td><a v-bind:href="`/rooms/${room.id}/edit`">(Edit)</a></td>
20
+ <td><a v-bind:href="`/rooms/${room.id}`" data-method="delete" data-confirm="Are you sure?" rel="nofollow">(Destroy)</a></td>
21
+ </tr>
22
+ </tbody>
23
+ </table>
24
+
25
+ <br>
26
+
27
+ <a href="/rooms/new">(New Room)</a>
28
+ </div>
29
+ </template>
30
+
31
+ <script>
32
+ import axios from 'axios'
33
+
34
+ export default {
35
+ name: 'Rooms',
36
+ data () {
37
+ return {
38
+ rooms: [],
39
+ notice: null
40
+ }
41
+ },
42
+ mounted () {
43
+ axios
44
+ .get('/rooms')
45
+ .then(response => {
46
+ this.rooms = response.data;
47
+ })
48
+ }
49
+ }
50
+ </script>
51
+
52
+ <style scoped>
53
+ </style>
@@ -0,0 +1,91 @@
1
+ <template>
2
+ <div v-if="room">
3
+ <p id="notice">{{ notice }}</p>
4
+
5
+ <p>
6
+ <strong>Name:</strong>
7
+ {{ room.name }}
8
+ </p>
9
+
10
+ <p>
11
+ <strong>Private Meeting:</strong>
12
+ <p><router-link v-bind:to="{ name: 'Meetings', params: { room_id: room.id } }">Show Meetings</router-link></p>
13
+ <p><a href="#" v-on:click="create_meeting(room)" data-remote="true">Create Meeting</a></p>
14
+ </p>
15
+
16
+ <p>
17
+ <strong>Members:</strong>
18
+ <div>
19
+ <table>
20
+ <tbody>
21
+ <tr v-for="member in room.members" :key="`${member.name}`">
22
+ <td>{{ member.name }}</td>
23
+ </tr>
24
+ </tbody>
25
+ </table>
26
+ </div>
27
+ </p>
28
+
29
+ <p>
30
+ <strong>Manage members:</strong>
31
+ <a v-bind:href="`/rooms/${room.id}`">(Show)</a>
32
+ </p>
33
+
34
+ <br>
35
+
36
+ <a v-bind:href="`/rooms/${room.id}/edit`">(Edit)</a>
37
+ <router-link v-bind:to="{ name: 'Rooms' }">Back</router-link>
38
+ </div>
39
+ </template>
40
+
41
+ <script>
42
+ import axios from 'axios'
43
+
44
+ export default {
45
+ name: 'Room',
46
+ props: {
47
+ room_id: {
48
+ required: true
49
+ },
50
+ message: String
51
+ },
52
+ data () {
53
+ return {
54
+ room: null,
55
+ notice: this.message
56
+ }
57
+ },
58
+ mounted () {
59
+ axios
60
+ .get(`/rooms/${this.room_id}`)
61
+ .then(response => {
62
+ this.room = response.data;
63
+ })
64
+ .catch(error => {
65
+ this.notice = error;
66
+ })
67
+ },
68
+ methods: {
69
+ create_meeting (room_id) {
70
+ axios
71
+ .post(`/rooms/${this.room_id}/meetings`)
72
+ .then(response => {
73
+ if (response.status == 201) {
74
+ let meeting_id = response.data.Meeting.MeetingId;
75
+ this.$router.push({ name: 'Meeting', params: { room_id: this.room_id, meeting_id: meeting_id, message: `Meeting <${meeting_id}> was successfully created.` } });
76
+ }
77
+ })
78
+ .catch (error => {
79
+ if (error.response.status == 403) {
80
+ this.notice = error.response.data.notice;
81
+ } else {
82
+ this.notice = error;
83
+ }
84
+ })
85
+ }
86
+ }
87
+ }
88
+ </script>
89
+
90
+ <style scoped>
91
+ </style>