Icu 9482 add no connection message for session recording with no connections (#294)

* refactor: 💡 add controller for channels-by-connection/index

* feat: 🎸 show message for new session that have no connections

✅ Closes: ICU-9482

* feat: 🎸 add message for failed session with no connections (#296)

✅ Closes: ICU-9489

* refactor: 💡 remove '-' from 'in progress' usage
This commit is contained in:
Cameron Perera authored and GitHub committed 2023-05-30 11:34:07 -05:00
1 parent 0aef13446b
commit 3d9836a55b
5 files changed
+282 -111

No files matched your search

@@ -234,6 +234,12 @@ session-recording:
title: Channels by connection
messages:
no-channels: There are no channels for this connection
started-no-connections:
title: Session Recording In Progress
description: The Session has started but no connections have been made.
unknown-no-connections:
title: Session Recording Failure
description: This Session cannot be played back because no connections were established, and the Session has failed.
channel:
title: Channel
title_index: Channel {index}
@@ -0,0 +1,31 @@
import Controller from '@ember/controller';
import {
STATE_SESSION_RECORDING_STARTED,
STATE_SESSION_RECORDING_UNKNOWN,
} from 'api/models/session-recording';
export default class ScopesScopeSessionRecordingsSessionRecordingChannelsByConnectionIndexController extends Controller {
/**
* Returns true if the session recording has started
* but has not started any connections
* @type {boolean}
*/
get isSessionInprogressWithNoConnections() {
return (
this.model?.sessionRecording?.connection_recordings?.length === 0 &&
this.model?.sessionRecording?.state === STATE_SESSION_RECORDING_STARTED
);
}
/**
* Returns true if the session recording state is unknown
* and has no connections
* @type {boolean}
*/
get isSessionUnknownWithNoConnections() {
return (
this.model?.sessionRecording?.connection_recordings?.length === 0 &&
this.model?.sessionRecording?.state === STATE_SESSION_RECORDING_UNKNOWN
);
}
}
@@ -11,119 +11,149 @@
<div class='channels-by-connection'>
<Layout::Sidebar as |sb|>
<sb.Body>
{{#each
@model.sessionRecording.connection_recordings
as |connection|
}}
<div class='channels-by-connection-connection-header'>
<p class='hds-typography-display-300 hds-font-weight-semibold'>
{{indexed-display-name
'resources.session-recording.connection.title_index'
@model.sessionRecording.connection_recordings
connection
{{#if this.isSessionInprogressWithNoConnections}}
<Hds::ApplicationState as |A|>
{{! Session recording in progress with no connections }}
<A.Header
@title={{t
'resources.session-recording.channels-by-connection.messages.started-no-connections.title'
}}
</p>
<Copyable
@text={{connection.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{connection.id}}</code>
</Copyable>
</div>
{{#if connection.channel_recordings}}
<Hds::Table
@model={{connection.channel_recordings}}
@density='tall'
@columns={{array
(hash
label=(t
'resources.session-recording.connection.form.channel.label'
)
)
(hash
label=(t
'resources.session-recording.connection.form.summary.label'
)
)
(hash label=(t 'form.id.label'))
(hash label='' align='right')
/>
<A.Body
@text={{t
'resources.session-recording.channels-by-connection.messages.started-no-connections.description'
}}
>
<:body as |B|>
<B.Tr>
<B.Td>
<span>
{{indexed-display-name
'resources.session-recording.channel.title_index'
connection.channel_recordings
B.data
}}
</span>
</B.Td>
<B.Td>
<MetadataList as |list|>
{{! Bytes up }}
{{#if B.data.bytes_up}}
<list.Item @icon='arrow-up' @color='success'>
{{t
'resources.session-recording.up'
bytes=(format-bytes-size B.data.bytes_up)
}}
</list.Item>
{{/if}}
{{! Bytes down }}
{{#if B.data.bytes_down}}
<list.Item @icon='arrow-down' @color='action'>
{{t
'resources.session-recording.down'
bytes=(format-bytes-size B.data.bytes_down)
}}
</list.Item>
{{/if}}
{{! Duration }}
{{#if B.data.duration}}
<list.Item @icon='clock' @color='faint'>
{{format-time-duration B.data.duration}}
</list.Item>
{{/if}}
</MetadataList>
</B.Td>
<B.Td>
<Copyable
@text={{B.data.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{B.data.id}}</code>
</Copyable>
</B.Td>
<B.Td align='right'>
{{#if @model.sessionRecording.isAvailable}}
<Hds::Button
@icon='play'
@color='secondary'
@text={{t 'actions.play'}}
@route='scopes.scope.session-recordings.session-recording.channels-by-connection.channel'
@model={{B.data.id}}
/>
{{/if}}
</B.Td>
</B.Tr>
</:body>
</Hds::Table>
{{else}}
<div class='no-channels-message'>
<Hds::Alert @type='compact' as |A|>
<A.Description>
{{t
'resources.session-recording.channels-by-connection.messages.no-channels'
}}
</A.Description>
</Hds::Alert>
/>
</Hds::ApplicationState>
{{else if this.isSessionUnknownWithNoConnections}}
<Hds::ApplicationState as |A|>
{{! Session recording unknown with no connections }}
<A.Header
@title={{t
'resources.session-recording.channels-by-connection.messages.unknown-no-connections.title'
}}
/>
<A.Body
@text={{t
'resources.session-recording.channels-by-connection.messages.unknown-no-connections.description'
}}
/>
</Hds::ApplicationState>
{{else}}
{{#each
@model.sessionRecording.connection_recordings
as |connection|
}}
<div class='channels-by-connection-connection-header'>
<p class='hds-typography-display-300 hds-font-weight-semibold'>
{{indexed-display-name
'resources.session-recording.connection.title_index'
@model.sessionRecording.connection_recordings
connection
}}
</p>
<Copyable
@text={{connection.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{connection.id}}</code>
</Copyable>
</div>
{{/if}}
{{/each}}
{{#if connection.channel_recordings}}
<Hds::Table
@model={{connection.channel_recordings}}
@density='tall'
@columns={{array
(hash
label=(t
'resources.session-recording.connection.form.channel.label'
)
)
(hash
label=(t
'resources.session-recording.connection.form.summary.label'
)
)
(hash label=(t 'form.id.label'))
(hash label='' align='right')
}}
>
<:body as |B|>
<B.Tr>
<B.Td>
<span>
{{indexed-display-name
'resources.session-recording.channel.title_index'
connection.channel_recordings
B.data
}}
</span>
</B.Td>
<B.Td>
<MetadataList as |list|>
{{! Bytes up }}
{{#if B.data.bytes_up}}
<list.Item @icon='arrow-up' @color='success'>
{{t
'resources.session-recording.up'
bytes=(format-bytes-size B.data.bytes_up)
}}
</list.Item>
{{/if}}
{{! Bytes down }}
{{#if B.data.bytes_down}}
<list.Item @icon='arrow-down' @color='action'>
{{t
'resources.session-recording.down'
bytes=(format-bytes-size B.data.bytes_down)
}}
</list.Item>
{{/if}}
{{! Duration }}
{{#if B.data.duration}}
<list.Item @icon='clock' @color='faint'>
{{format-time-duration B.data.duration}}
</list.Item>
{{/if}}
</MetadataList>
</B.Td>
<B.Td>
<Copyable
@text={{B.data.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{B.data.id}}</code>
</Copyable>
</B.Td>
<B.Td align='right'>
{{#if @model.sessionRecording.isAvailable}}
<Hds::Button
@icon='play'
@color='secondary'
@text={{t 'actions.play'}}
@route='scopes.scope.session-recordings.session-recording.channels-by-connection.channel'
@model={{B.data.id}}
/>
{{/if}}
</B.Td>
</B.Tr>
</:body>
</Hds::Table>
{{else}}
<div class='no-channels-message'>
<Hds::Alert @type='compact' as |A|>
<A.Description>
{{t
'resources.session-recording.channels-by-connection.messages.no-channels'
}}
</A.Description>
</Hds::Alert>
</div>
{{/if}}
{{/each}}
{{/if}}
</sb.Body>
<sb.Sidebar>
<div class='layout-sidebar-sidebar-details'>
@@ -30,7 +30,7 @@ module('Integration | Component | session-recording/status', function (hooks) {
assert.dom(BADGE_TEXT_SELECTOR).hasText('Completed');
});
test('it renders an in-progress status badge', async function (assert) {
test('it renders an in progress status badge', async function (assert) {
assert.expect(2);
this.status = STATE_SESSION_RECORDING_STARTED;
@@ -0,0 +1,104 @@
import { module, test } from 'qunit';
import { setupTest } from 'admin/tests/helpers';
import {
STATE_SESSION_RECORDING_AVAILABLE,
STATE_SESSION_RECORDING_STARTED,
STATE_SESSION_RECORDING_UNKNOWN,
} from 'api/models/session-recording';
module(
'Unit | Controller | scopes/scope/session-recordings/session-recording/channels-by-connection/index',
function (hooks) {
setupTest(hooks);
test('isSessionInprogressWithNoConnections returns true if session has started with no connections', function (assert) {
const store = this.owner.lookup('service:store');
const controller = this.owner.lookup(
'controller:scopes/scope/session-recordings/session-recording/channels-by-connection/index'
);
const sessionRecording = store.createRecord('session-recording', {
state: STATE_SESSION_RECORDING_STARTED,
connection_recordings: [],
});
controller.set('model', { sessionRecording });
assert.true(controller.isSessionInprogressWithNoConnections);
});
test('isSessionInprogressWithNoConnections returns false if session has started and has connections', function (assert) {
const store = this.owner.lookup('service:store');
const controller = this.owner.lookup(
'controller:scopes/scope/session-recordings/session-recording/channels-by-connection/index'
);
const sessionRecording = store.createRecord('session-recording', {
state: STATE_SESSION_RECORDING_STARTED,
connection_recordings: [store.createRecord('connection-recording')],
});
controller.set('model', { sessionRecording });
assert.false(controller.isSessionInprogressWithNoConnections);
});
test('isSessionInprogressWithNoConnections returns false if session is complete and has connections', function (assert) {
const store = this.owner.lookup('service:store');
const controller = this.owner.lookup(
'controller:scopes/scope/session-recordings/session-recording/channels-by-connection/index'
);
const sessionRecording = store.createRecord('session-recording', {
state: STATE_SESSION_RECORDING_AVAILABLE,
connection_recordings: [store.createRecord('connection-recording')],
});
controller.set('model', { sessionRecording });
assert.false(controller.isSessionInprogressWithNoConnections);
});
test('isSessionUnknownWithNoConnections returns true if session is unknown with no connections', function (assert) {
const store = this.owner.lookup('service:store');
const controller = this.owner.lookup(
'controller:scopes/scope/session-recordings/session-recording/channels-by-connection/index'
);
const sessionRecording = store.createRecord('session-recording', {
state: STATE_SESSION_RECORDING_UNKNOWN,
connection_recordings: [],
});
controller.set('model', { sessionRecording });
assert.true(controller.isSessionUnknownWithNoConnections);
});
test('isSessionUnknownWithNoConnections returns false if session is unknown and has connections', function (assert) {
const store = this.owner.lookup('service:store');
const controller = this.owner.lookup(
'controller:scopes/scope/session-recordings/session-recording/channels-by-connection/index'
);
const sessionRecording = store.createRecord('session-recording', {
state: STATE_SESSION_RECORDING_UNKNOWN,
connection_recordings: [store.createRecord('connection-recording')],
});
controller.set('model', { sessionRecording });
assert.false(controller.isSessionUnknownWithNoConnections);
});
test('isSessionUnknownWithNoConnections returns false if session is complete and has connections', function (assert) {
const store = this.owner.lookup('service:store');
const controller = this.owner.lookup(
'controller:scopes/scope/session-recordings/session-recording/channels-by-connection/index'
);
const sessionRecording = store.createRecord('session-recording', {
state: STATE_SESSION_RECORDING_AVAILABLE,
connection_recordings: [store.createRecord('connection-recording')],
});
controller.set('model', { sessionRecording });
assert.false(controller.isSessionUnknownWithNoConnections);
});
}
);