diff --git a/models/tracker/src/index.ts b/models/tracker/src/index.ts
index ea5e3e99d9..e8678e0cb8 100644
--- a/models/tracker/src/index.ts
+++ b/models/tracker/src/index.ts
@@ -1241,6 +1241,10 @@ export function createModel (builder: Builder): void {
     filters: ['priority', 'assignee', 'project', 'sprint', 'modifiedOn']
   })
 
+  builder.mixin(tracker.class.Sprint, core.class.Class, view.mixin.ClassFilters, {
+    filters: ['status', 'project', 'lead', 'startDate', 'targetDate', 'modifiedOn', 'capacity']
+  })
+
   builder.createDoc(
     presentation.class.ObjectSearchCategory,
     core.space.Model,
@@ -1600,4 +1604,78 @@ export function createModel (builder: Builder): void {
     view.component.NumberPresenter,
     tracker.component.ReportedTimeEditor
   )
+
+  const sprintOptions: ViewOptionsModel = {
+    groupBy: ['project', 'lead'],
+    orderBy: [
+      ['startDate', SortingOrder.Descending],
+      ['modifiedOn', SortingOrder.Descending],
+      ['targetDate', SortingOrder.Descending],
+      ['capacity', SortingOrder.Ascending]
+    ],
+    other: []
+  }
+
+  builder.createDoc(view.class.Viewlet, core.space.Model, {
+    attachTo: tracker.class.Sprint,
+    descriptor: view.viewlet.List,
+    viewOptions: sprintOptions,
+    config: [
+      {
+        key: '',
+        presenter: tracker.component.SprintStatusPresenter,
+        props: { width: '1rem', kind: 'list', size: 'small', justify: 'center' }
+      },
+      { key: '', presenter: tracker.component.SprintPresenter, props: { shouldUseMargin: true } },
+      { key: '', presenter: view.component.GrowPresenter, props: { type: 'grow' } },
+      { key: '', presenter: tracker.component.SprintProjectEditor, props: { kind: 'list' } },
+      {
+        key: '',
+        presenter: contact.component.MembersPresenter,
+        props: {
+          kind: 'link',
+          intlTitle: tracker.string.SprintMembersTitle,
+          intlSearchPh: tracker.string.SprintMembersSearchPlaceholder
+        }
+      },
+      { key: '', presenter: tracker.component.SprintDatePresenter, props: { field: 'startDate' } },
+      { key: '', presenter: tracker.component.SprintDatePresenter, props: { field: 'targetDate' } },
+      {
+        key: '$lookup.lead',
+        presenter: tracker.component.SprintLeadPresenter,
+        props: {
+          _class: tracker.class.Sprint,
+          defaultClass: contact.class.Employee,
+          shouldShowLabel: false,
+          size: 'x-small'
+        }
+      }
+    ]
+  })
+
+  createAction(
+    builder,
+    {
+      action: view.actionImpl.ValueSelector,
+      actionPopup: view.component.ValueSelector,
+      actionProps: {
+        attribute: 'lead',
+        _class: contact.class.Employee,
+        query: {},
+        placeholder: tracker.string.SprintLead
+      },
+      label: tracker.string.SprintLead,
+      icon: contact.icon.Person,
+      keyBinding: [],
+      input: 'none',
+      category: tracker.category.Tracker,
+      target: tracker.class.Sprint,
+      context: {
+        mode: ['context'],
+        application: tracker.app.Tracker,
+        group: 'edit'
+      }
+    },
+    tracker.action.SetSprintLead
+  )
 }
diff --git a/models/tracker/src/plugin.ts b/models/tracker/src/plugin.ts
index 1f69e517be..dd4737dd0d 100644
--- a/models/tracker/src/plugin.ts
+++ b/models/tracker/src/plugin.ts
@@ -62,6 +62,7 @@ export default mergeIds(trackerId, tracker, {
   },
   action: {
     NewRelatedIssue: '' as Ref<Action<Doc, Record<string, any>>>,
-    DeleteSprint: '' as Ref<Action<Doc, Record<string, any>>>
+    DeleteSprint: '' as Ref<Action<Doc, Record<string, any>>>,
+    SetSprintLead: '' as Ref<Action<Doc, Record<string, any>>>
   }
 })
diff --git a/plugins/hr-resources/src/components/ScheduleRequests.svelte b/plugins/hr-resources/src/components/ScheduleRequests.svelte
index 47206a36e4..00adb3d12d 100644
--- a/plugins/hr-resources/src/components/ScheduleRequests.svelte
+++ b/plugins/hr-resources/src/components/ScheduleRequests.svelte
@@ -51,6 +51,7 @@
   {#each requests as request}
     {#await getType(request) then type}
       {#if type}
+        <!-- svelte-ignore a11y-click-events-have-key-events -->
         <div
           class="request flex-center"
           class:cursor-pointer={editable}
diff --git a/plugins/hr-resources/src/components/ScheduleView.svelte b/plugins/hr-resources/src/components/ScheduleView.svelte
index cfa7637d54..75aa356dc1 100644
--- a/plugins/hr-resources/src/components/ScheduleView.svelte
+++ b/plugins/hr-resources/src/components/ScheduleView.svelte
@@ -148,7 +148,7 @@
 
   const reportQuery = createQuery()
 
-  import tracker from '@hcengineering/tracker'
+  import tracker, { Issue } from '@hcengineering/tracker'
   import { EmployeeReports, fromTzDate, getEndDate, getStartDate } from '../utils'
 
   let timeReports: Map<Ref<Employee>, EmployeeReports> = new Map()
@@ -163,17 +163,24 @@
       const newMap = new Map<Ref<Employee>, EmployeeReports>()
       for (const r of res) {
         if (r.employee != null) {
-          const or = newMap.get(r.employee)
-          newMap.set(r.employee, { value: (or?.value ?? 0) + r.value, reports: [...(or?.reports ?? []), r] })
+          const or = newMap.get(r.employee) ?? {
+            value: 0,
+            reports: [],
+            tasks: new Map()
+          }
+          const tsk = r.$lookup?.attachedTo as Issue
+          newMap.set(r.employee, {
+            value: or.value + r.value,
+            reports: [...or.reports, r],
+            tasks: or.tasks.set(tsk._id, tsk)
+          })
         }
       }
       timeReports = newMap
     },
     {
       lookup: {
-        _id: {
-          attachedTo: tracker.class.Issue
-        }
+        attachedTo: tracker.class.Issue
       }
     }
   )
diff --git a/plugins/hr-resources/src/components/schedule/MonthTableView.svelte b/plugins/hr-resources/src/components/schedule/MonthTableView.svelte
index 971aaf0d69..5e13b1ae84 100644
--- a/plugins/hr-resources/src/components/schedule/MonthTableView.svelte
+++ b/plugins/hr-resources/src/components/schedule/MonthTableView.svelte
@@ -83,6 +83,18 @@
   function getOverrideConfig (startDate: Date): Map<string, BuildModelKey> {
     const typevals = getTypeVals(startDate)
     const endDate = getEndDate(startDate.getFullYear(), startDate.getMonth())
+
+    const getReport = (id: Ref<Doc>): EmployeeReports => {
+      return timeReports.get(id as Ref<Employee>) ?? { value: 0, reports: [], tasks: new Map() }
+    }
+    const getTPD = (id: Ref<Doc>): number => {
+      const rr = getReport(id)
+      if (rr.value === 0) {
+        return 0
+      }
+      return rr.tasks.size / rr.value
+    }
+
     return new Map<string, BuildModelKey>([
       [
         '@wdCount',
@@ -109,12 +121,38 @@
           presenter: ReportPresenter,
           props: {
             month: startDate ?? getStartDate(currentDate.getFullYear(), currentDate.getMonth()),
-            display: (staff: Staff) => (timeReports.get(staff._id) ?? { value: 0 }).value
+            display: (staff: Staff) => getReport(staff._id).value
           },
-          sortingKey: '@wdCount',
-          sortingFunction: (a: Doc, b: Doc) =>
-            getTotal(getStatRequests(b._id as Ref<Staff>, startDate), startDate, endDate, types) -
-            getTotal(getStatRequests(a._id as Ref<Staff>, startDate), startDate, endDate, types)
+          sortingKey: '@wdCountReported',
+          sortingFunction: (a: Doc, b: Doc) => getReport(b._id).value - getReport(a._id).value
+        }
+      ],
+      [
+        '@wdTaskCountReported',
+        {
+          key: '',
+          label: getEmbeddedLabel('Tasks'),
+          presenter: ReportPresenter,
+          props: {
+            month: startDate ?? getStartDate(currentDate.getFullYear(), currentDate.getMonth()),
+            display: (staff: Staff) => getReport(staff._id).tasks.size
+          },
+          sortingKey: '@wdTaskCountReported',
+          sortingFunction: (a: Doc, b: Doc) => getReport(b._id).tasks.size - getReport(a._id).tasks.size
+        }
+      ],
+      [
+        '@wdTaskPerDayReported',
+        {
+          key: '',
+          label: getEmbeddedLabel('TPD'),
+          presenter: ReportPresenter,
+          props: {
+            month: startDate ?? getStartDate(currentDate.getFullYear(), currentDate.getMonth()),
+            display: (staff: Staff) => getTPD(staff._id)
+          },
+          sortingKey: '@wdTaskPerDayReported',
+          sortingFunction: (a: Doc, b: Doc) => getTPD(b._id) - getTPD(a._id)
         }
       ],
       [
diff --git a/plugins/hr-resources/src/components/schedule/MonthView.svelte b/plugins/hr-resources/src/components/schedule/MonthView.svelte
index 73c6d926e5..a2aad648bb 100644
--- a/plugins/hr-resources/src/components/schedule/MonthView.svelte
+++ b/plugins/hr-resources/src/components/schedule/MonthView.svelte
@@ -39,6 +39,7 @@
   import CreateRequest from '../CreateRequest.svelte'
   import RequestsPopup from '../RequestsPopup.svelte'
   import ScheduleRequests from '../ScheduleRequests.svelte'
+  import ReportsPopup from './ReportsPopup.svelte'
 
   export let currentDate: Date = new Date()
 
@@ -106,6 +107,13 @@
       bottom: 3.5
     }
   }
+
+  function showReportInfo (employee: Staff, rTime: EmployeeReports | undefined): void {
+    if (rTime === undefined) {
+      return
+    }
+    showPopup(ReportsPopup, { employee, reports: rTime.reports }, 'top')
+  }
 </script>
 
 {#if departmentStaff.length}
@@ -152,9 +160,14 @@
             >
               {getTotal(requests, startDate, endDate, types)}
             </td>
-            <td class="p-1 text-center">
+            <!-- svelte-ignore a11y-click-events-have-key-events -->
+            <td
+              class="p-1 text-center whitespace-nowrap cursor-pointer"
+              on:click={() => showReportInfo(employee, rTime)}
+            >
               {#if rTime !== undefined}
                 {floorFractionDigits(rTime.value, 3)}
+                ({rTime.tasks.size})
               {:else}
                 0
               {/if}
@@ -166,6 +179,7 @@
               {@const tooltipValue = getTooltip(requests)}
               {@const ww = findReports(employee, day, timeReports)}
               {#key [tooltipValue, editable]}
+                <!-- svelte-ignore a11y-click-events-have-key-events -->
                 <td
                   class="w-9 max-w-9 min-w-9"
                   class:today={areDatesEqual(todayDate, day)}
diff --git a/plugins/hr-resources/src/components/schedule/ReportsPopup.svelte b/plugins/hr-resources/src/components/schedule/ReportsPopup.svelte
new file mode 100644
index 0000000000..95fb875fa1
--- /dev/null
+++ b/plugins/hr-resources/src/components/schedule/ReportsPopup.svelte
@@ -0,0 +1,59 @@
+<!--
+// Copyright © 2022 Hardcore Engineering Inc.
+// 
+// Licensed under the Eclipse Public License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License. You may
+// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
+// 
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// 
+// See the License for the specific language governing permissions and
+// limitations under the License.
+-->
+<script lang="ts">
+  import contact, { Employee } from '@hcengineering/contact'
+  import { EmployeePresenter } from '@hcengineering/contact-resources'
+  import { FindOptions } from '@hcengineering/core'
+  import { getEmbeddedLabel } from '@hcengineering/platform'
+  import presentation, { Card } from '@hcengineering/presentation'
+  import tracker, { TimeSpendReport } from '@hcengineering/tracker'
+  import { TableBrowser } from '@hcengineering/view-resources'
+
+  export let reports: TimeSpendReport[]
+  export let employee: Employee
+
+  export function canClose (): boolean {
+    return true
+  }
+  const options: FindOptions<TimeSpendReport> = {
+    lookup: {
+      attachedTo: tracker.class.Issue,
+      employee: contact.class.Employee
+    },
+    sort: {
+      date: -1
+    }
+  }
+</script>
+
+<Card
+  label={getEmbeddedLabel('Reports')}
+  canSave={true}
+  on:close
+  on:changeContent
+  okAction={() => {}}
+  okLabel={presentation.string.Ok}
+>
+  <svelte:fragment slot="header">
+    <EmployeePresenter value={employee} disableClick />
+  </svelte:fragment>
+  <TableBrowser
+    showFilterBar={false}
+    _class={tracker.class.TimeSpendReport}
+    query={{ _id: { $in: reports.map((it) => it._id) } }}
+    config={['$lookup.attachedTo', '$lookup.attachedTo.title', '', 'employee', 'date']}
+    {options}
+  />
+</Card>
diff --git a/plugins/hr-resources/src/utils.ts b/plugins/hr-resources/src/utils.ts
index 3a07d3fd0f..d27bdd122c 100644
--- a/plugins/hr-resources/src/utils.ts
+++ b/plugins/hr-resources/src/utils.ts
@@ -2,7 +2,7 @@ import { Employee, formatName } from '@hcengineering/contact'
 import { Ref, TxOperations } from '@hcengineering/core'
 import { Department, Request, RequestType, Staff, TzDate } from '@hcengineering/hr'
 import { MessageBox } from '@hcengineering/presentation'
-import { TimeSpendReport } from '@hcengineering/tracker'
+import { Issue, TimeSpendReport } from '@hcengineering/tracker'
 import { isWeekend, MILLISECONDS_IN_DAY, showPopup } from '@hcengineering/ui'
 import hr from './plugin'
 
@@ -235,5 +235,6 @@ export function tableToCSV (tableId: string, separator = ','): string {
 
 export interface EmployeeReports {
   reports: TimeSpendReport[]
+  tasks: Map<Ref<Issue>, Issue>
   value: number
 }
diff --git a/plugins/tracker-resources/src/components/issues/timereport/ReportsPopup.svelte b/plugins/tracker-resources/src/components/issues/timereport/ReportsPopup.svelte
index b43c143604..7328c21f18 100644
--- a/plugins/tracker-resources/src/components/issues/timereport/ReportsPopup.svelte
+++ b/plugins/tracker-resources/src/components/issues/timereport/ReportsPopup.svelte
@@ -66,7 +66,7 @@
       <TableBrowser
         showFilterBar={false}
         _class={tracker.class.TimeSpendReport}
-        query={{ attachedTo: { $in: [issue._id, ...issue.childInfo.map((it) => it.childId)] } }}
+        query={{ attachedTo: { $in: [issue._id, ...(issue.childInfo?.map((it) => it.childId) ?? [])] } }}
         config={[
           '$lookup.attachedTo',
           '',
diff --git a/plugins/tracker-resources/src/components/projects/LeadPresenter.svelte b/plugins/tracker-resources/src/components/projects/LeadPresenter.svelte
index c3828480e2..7dc4b509fb 100644
--- a/plugins/tracker-resources/src/components/projects/LeadPresenter.svelte
+++ b/plugins/tracker-resources/src/components/projects/LeadPresenter.svelte
@@ -27,7 +27,7 @@
   export let value: Employee | null
   export let _class: Ref<Class<Project | Sprint>>
   export let size: IconSize = 'x-small'
-  export let parentId: Ref<Project>
+  export let parentId: Ref<Doc>
   export let defaultClass: Ref<Class<Doc>> | undefined = undefined
   export let isEditable: boolean = true
   export let shouldShowLabel: boolean = false
@@ -54,7 +54,7 @@
       return
     }
 
-    const currentParent = await client.findOne(_class, { _id: parentId })
+    const currentParent = await client.findOne(_class, { _id: parentId as Ref<Project> })
 
     if (currentParent === undefined) {
       return
diff --git a/plugins/tracker-resources/src/components/sprints/SprintBrowser.svelte b/plugins/tracker-resources/src/components/sprints/SprintBrowser.svelte
index 8057f8f4de..173b72f911 100644
--- a/plugins/tracker-resources/src/components/sprints/SprintBrowser.svelte
+++ b/plugins/tracker-resources/src/components/sprints/SprintBrowser.svelte
@@ -13,62 +13,84 @@
 // limitations under the License.
 -->
 <script lang="ts">
-  import contact from '@hcengineering/contact'
-  import { DocumentQuery, FindOptions, SortingOrder, WithLookup } from '@hcengineering/core'
+  import { DocumentQuery, WithLookup } from '@hcengineering/core'
   import { IntlString } from '@hcengineering/platform'
-  import { createQuery } from '@hcengineering/presentation'
+  import { getClient } from '@hcengineering/presentation'
   import { Sprint } from '@hcengineering/tracker'
-  import { Button, defaultSP, Icon, IconAdd, Label, Scroller, showPopup } from '@hcengineering/ui'
+  import { Button, IconAdd, Label, SearchEdit, showPopup } from '@hcengineering/ui'
+  import view, { Viewlet } from '@hcengineering/view'
+  import {
+    FilterBar,
+    FilterButton,
+    getActiveViewletId,
+    getViewOptions,
+    setActiveViewletId,
+    ViewletSettingButton
+  } from '@hcengineering/view-resources'
   import tracker from '../../plugin'
   import { getIncludedSprintStatuses, sprintTitleMap, SprintViewMode } from '../../utils'
   import NewSprint from './NewSprint.svelte'
-  import SprintDatePresenter from './SprintDatePresenter.svelte'
-  import SprintListBrowser from './SprintListBrowser.svelte'
-  import SprintProjectEditor from './SprintProjectEditor.svelte'
+  import SprintContent from './SprintContent.svelte'
 
   export let label: IntlString
   export let query: DocumentQuery<Sprint> = {}
   export let search: string = ''
   export let mode: SprintViewMode = 'all'
 
-  const ENTRIES_LIMIT = 200
-  const resultSprintsQuery = createQuery()
-
-  const sprintOptions: FindOptions<Sprint> = {
-    sort: { startDate: SortingOrder.Descending },
-    limit: ENTRIES_LIMIT,
-    lookup: {
-      lead: contact.class.Employee,
-      project: tracker.class.Project
-    }
+  const space = typeof query.space === 'string' ? query.space : tracker.team.DefaultTeam
+  const showCreateDialog = async () => {
+    showPopup(NewSprint, { space, targetElement: null }, 'top')
   }
 
-  let resultSprints: WithLookup<Sprint>[] = []
+  export let panelWidth: number = 0
+
+  let viewlet: WithLookup<Viewlet> | undefined = undefined
+
+  let searchQuery: DocumentQuery<Sprint> = { ...query }
+  function updateSearchQuery (search: string): void {
+    searchQuery = search === '' ? { ...query } : { ...query, $search: search }
+  }
+  $: if (query) updateSearchQuery(search)
 
   $: includedSprintStatuses = getIncludedSprintStatuses(mode)
   $: title = sprintTitleMap[mode]
   $: includedSprintsQuery = { status: { $in: includedSprintStatuses } }
 
-  $: baseQuery = {
-    ...includedSprintsQuery,
-    ...query
+  const client = getClient()
+  let resultQuery: DocumentQuery<Sprint> = { ...searchQuery }
+
+  let viewlets: WithLookup<Viewlet>[] = []
+
+  $: update()
+
+  async function update (): Promise<void> {
+    viewlets = await client.findAll(
+      view.class.Viewlet,
+      { attachTo: tracker.class.Sprint },
+      {
+        lookup: {
+          descriptor: view.class.ViewletDescriptor
+        }
+      }
+    )
+    const _id = getActiveViewletId()
+    viewlet = viewlets.find((viewlet) => viewlet._id === _id) || viewlets[0]
+    setActiveViewletId(viewlet._id)
   }
 
-  $: resultQuery = search === '' ? baseQuery : { $search: search, ...baseQuery }
-
-  $: resultSprintsQuery.query<Sprint>(
-    tracker.class.Sprint,
-    { ...resultQuery },
-    (result) => {
-      resultSprints = result
-    },
-    sprintOptions
-  )
-
-  const space = typeof query.space === 'string' ? query.space : tracker.team.DefaultTeam
-  const showCreateDialog = async () => {
-    showPopup(NewSprint, { space, targetElement: null }, 'top')
+  let asideFloat: boolean = false
+  let asideShown: boolean = true
+  $: if (panelWidth < 900 && !asideFloat) asideFloat = true
+  $: if (panelWidth >= 900 && asideFloat) {
+    asideFloat = false
+    asideShown = false
   }
+  let docWidth: number
+  let docSize: boolean = false
+  $: if (docWidth <= 900 && !docSize) docSize = true
+  $: if (docWidth > 900 && docSize) docSize = false
+
+  $: viewOptions = getViewOptions(viewlet)
 
   const handleViewModeChanged = (newMode: SprintViewMode) => {
     if (newMode === undefined || newMode === mode) {
@@ -77,18 +99,25 @@
 
     mode = newMode
   }
-
-  const retrieveMembers = (s: Sprint) => s.members
 </script>
 
 <div class="fs-title flex-between header">
-  <div class="flex-center">
+  <div class="flex-row-center">
     <Label {label} />
     <div class="projectTitle">
       › <Label label={title} />
     </div>
+    <div class="ml-4">
+      <FilterButton _class={tracker.class.Issue} {space} />
+    </div>
+  </div>
+  <div class="flex-row-center gap-2">
+    <SearchEdit bind:value={search} on:change={() => {}} />
+    <Button size="small" icon={IconAdd} label={tracker.string.Sprint} kind={'primary'} on:click={showCreateDialog} />
+    {#if viewlet}
+      <ViewletSettingButton bind:viewOptions {viewlet} />
+    {/if}
   </div>
-  <Button size="small" icon={IconAdd} label={tracker.string.Sprint} kind={'primary'} on:click={showCreateDialog} />
 </div>
 <div class="itemsContainer">
   <div class="flex-center">
@@ -130,66 +159,23 @@
         />
       </div>
     </div>
-    <!-- <div class="ml-3 filterButton">
-        <Button
-          size="small"
-          icon={IconAdd}
-          kind={'link-bordered'}
-          borderStyle={'dashed'}
-          label={tracker.string.Filter}
-          on:click={() => {}}
-        />
-      </div> -->
   </div>
-  <!-- <div class="flex-center">
-      <div class="flex-center">
-        <div class="buttonWrapper">
-          <Button selected size="small" shape="rectangle-right" icon={tracker.icon.ProjectsList} />
-        </div>
-        <div class="buttonWrapper">
-          <Button size="small" shape="rectangle-left" icon={tracker.icon.ProjectsTimeline} />
-        </div>
-      </div>
-      <div class="ml-3">
-        <Button size="small" icon={IconOptions} />
-      </div>
-    </div> -->
 </div>
-<div class="w-full h-full clear-mins">
-  <Scroller fade={defaultSP}>
-    <SprintListBrowser
-      _class={tracker.class.Sprint}
-      itemsConfig={[
-        { key: '', presenter: Icon, props: { icon: tracker.icon.Sprint, size: 'small' } },
-        { key: '', presenter: tracker.component.SprintPresenter, props: { kind: 'list' } },
-        { key: '', presenter: SprintProjectEditor, props: { kind: 'list' } },
-        {
-          key: '$lookup.lead',
-          presenter: tracker.component.LeadPresenter,
-          props: {
-            _class: tracker.class.Sprint,
-            defaultClass: contact.class.Employee,
-            shouldShowLabel: false,
-            size: 'x-small'
-          }
-        },
-        {
-          key: '',
-          presenter: contact.component.MembersPresenter,
-          props: {
-            kind: 'link',
-            intlTitle: tracker.string.SprintMembersTitle,
-            intlSearchPh: tracker.string.SprintMembersSearchPlaceholder,
-            retrieveMembers
-          }
-        },
-        { key: '', presenter: SprintDatePresenter, props: { field: 'startDate' } },
-        { key: '', presenter: SprintDatePresenter, props: { field: 'targetDate' } },
-        { key: '', presenter: tracker.component.SprintStatusPresenter }
-      ]}
-      sprints={resultSprints}
-    />
-  </Scroller>
+<FilterBar
+  _class={tracker.class.Sprint}
+  query={searchQuery}
+  {viewOptions}
+  on:change={(e) => (resultQuery = e.detail)}
+/>
+<div class="flex w-full h-full clear-mins">
+  {#if viewlet}
+    <SprintContent {viewlet} query={{ ...resultQuery, ...includedSprintsQuery }} {space} {viewOptions} />
+  {/if}
+  {#if $$slots.aside !== undefined && asideShown}
+    <div class="popupPanel-body__aside flex" class:float={asideFloat} class:shown={asideShown}>
+      <slot name="aside" />
+    </div>
+  {/if}
 </div>
 
 <style lang="scss">
@@ -220,8 +206,4 @@
       margin-right: 0;
     }
   }
-
-  // .filterButton {
-  //   color: var(--caption-color);
-  // }
 </style>
diff --git a/plugins/tracker-resources/src/components/sprints/SprintContent.svelte b/plugins/tracker-resources/src/components/sprints/SprintContent.svelte
new file mode 100644
index 0000000000..f8122b1c33
--- /dev/null
+++ b/plugins/tracker-resources/src/components/sprints/SprintContent.svelte
@@ -0,0 +1,51 @@
+<script lang="ts">
+  import contact from '@hcengineering/contact'
+  import { DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
+  import { Sprint } from '@hcengineering/tracker'
+  import { Component } from '@hcengineering/ui'
+  import { BuildModelKey, Viewlet, ViewOptions } from '@hcengineering/view'
+  import tracker from '../../plugin'
+  import NewSprint from './NewSprint.svelte'
+
+  export let viewlet: WithLookup<Viewlet>
+  export let query: DocumentQuery<Sprint> = {}
+  export let space: Ref<Space> | undefined
+
+  // Extra properties
+  export let viewOptions: ViewOptions
+
+  const createItemDialog = NewSprint
+  const createItemLabel = tracker.string.CreateSprint
+
+  const retrieveMembers = (s: Sprint) => s.members
+
+  function updateConfig (config: (string | BuildModelKey)[]): (string | BuildModelKey)[] {
+    return config.map((it) => {
+      if (typeof it === 'string') {
+        return it
+      }
+      return it.presenter === contact.component.MembersPresenter
+        ? { ...it, props: { ...it.props, retrieveMembers } }
+        : it
+    })
+  }
+</script>
+
+{#if viewlet?.$lookup?.descriptor?.component}
+  <Component
+    is={viewlet.$lookup.descriptor.component}
+    props={{
+      _class: tracker.class.Sprint,
+      config: updateConfig(viewlet.config),
+      options: viewlet.options,
+      createItemDialog,
+      createItemLabel,
+      viewlet,
+      viewOptions,
+      viewOptionsConfig: viewlet.viewOptions?.other,
+      space,
+      query,
+      props: {}
+    }}
+  />
+{/if}
diff --git a/plugins/tracker-resources/src/components/sprints/SprintLeadPresenter.svelte b/plugins/tracker-resources/src/components/sprints/SprintLeadPresenter.svelte
new file mode 100644
index 0000000000..96d6a5298b
--- /dev/null
+++ b/plugins/tracker-resources/src/components/sprints/SprintLeadPresenter.svelte
@@ -0,0 +1,105 @@
+<!--
+// Copyright © 2022 Hardcore Engineering Inc.
+// 
+// Licensed under the Eclipse Public License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License. You may
+// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
+// 
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// 
+// See the License for the specific language governing permissions and
+// limitations under the License.
+-->
+<script lang="ts">
+  import contact, { Employee } from '@hcengineering/contact'
+  import { Class, Doc, Ref } from '@hcengineering/core'
+  import { IntlString } from '@hcengineering/platform'
+  import { getClient, UsersPopup } from '@hcengineering/presentation'
+  import { Sprint } from '@hcengineering/tracker'
+  import { eventToHTMLElement, IconSize, showPopup } from '@hcengineering/ui'
+  import { AttributeModel } from '@hcengineering/view'
+  import { getObjectPresenter } from '@hcengineering/view-resources'
+  import tracker from '../../plugin'
+  import LeadPopup from '../projects/LeadPopup.svelte'
+
+  export let value: Employee | null
+  export let size: IconSize = 'x-small'
+  export let object: Sprint
+  export let defaultClass: Ref<Class<Doc>> | undefined = undefined
+  export let isEditable: boolean = true
+  export let shouldShowLabel: boolean = false
+  export let defaultName: IntlString | undefined = undefined
+
+  const client = getClient()
+
+  let presenter: AttributeModel | undefined
+
+  $: if (value || defaultClass) {
+    if (value) {
+      getObjectPresenter(client, value._class, { key: '' }).then((p) => {
+        presenter = p
+      })
+    } else if (defaultClass) {
+      getObjectPresenter(client, defaultClass, { key: '' }).then((p) => {
+        presenter = p
+      })
+    }
+  }
+
+  const handleLeadChanged = async (result: Employee | null | undefined) => {
+    if (!isEditable || result === undefined) {
+      return
+    }
+    const newLead = result === null ? null : result._id
+
+    await client.update(object, { lead: newLead })
+  }
+
+  const handleLeadEditorOpened = async (event: MouseEvent) => {
+    if (!isEditable) {
+      return
+    }
+    showPopup(
+      UsersPopup,
+      {
+        _class: contact.class.Employee,
+        selected: value?._id,
+        docQuery: {
+          active: true
+        },
+        allowDeselect: true,
+        placeholder: tracker.string.ProjectLeadSearchPlaceholder
+      },
+      eventToHTMLElement(event),
+      handleLeadChanged
+    )
+  }
+</script>
+
+{#if value && presenter}
+  <svelte:component
+    this={presenter.presenter}
+    {value}
+    {defaultName}
+    avatarSize={size}
+    isInteractive={true}
+    shouldShowPlaceholder={true}
+    shouldShowName={shouldShowLabel}
+    onEmployeeEdit={handleLeadEditorOpened}
+    tooltipLabels={{ component: LeadPopup, props: { lead: value } }}
+  />
+{:else if presenter}
+  <svelte:component
+    this={presenter.presenter}
+    {value}
+    {defaultName}
+    avatarSize={size}
+    isInteractive={true}
+    shouldShowPlaceholder={true}
+    shouldShowName={shouldShowLabel}
+    onEmployeeEdit={handleLeadEditorOpened}
+    tooltipLabels={{ personLabel: tracker.string.AssignedTo, placeholderLabel: tracker.string.AssignTo }}
+  />
+{/if}
diff --git a/plugins/tracker-resources/src/components/sprints/SprintList.svelte b/plugins/tracker-resources/src/components/sprints/SprintList.svelte
deleted file mode 100644
index 26bd0320b5..0000000000
--- a/plugins/tracker-resources/src/components/sprints/SprintList.svelte
+++ /dev/null
@@ -1,292 +0,0 @@
-<!--
-// Copyright © 2022 Hardcore Engineering Inc.
-// 
-// Licensed under the Eclipse Public License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License. You may
-// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
-// 
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// 
-// See the License for the specific language governing permissions and
-// limitations under the License.
--->
-<script lang="ts">
-  import contact from '@hcengineering/contact'
-  import { Class, Doc, FindOptions, getObjectValue, Ref, WithLookup } from '@hcengineering/core'
-  import { getClient } from '@hcengineering/presentation'
-  import { Issue, Project, Sprint } from '@hcengineering/tracker'
-  import { CheckBox, ExpandCollapse, Spinner, tooltip } from '@hcengineering/ui'
-  import { BuildModelKey } from '@hcengineering/view'
-  import { buildModel, LoadingProps } from '@hcengineering/view-resources'
-  import { createEventDispatcher } from 'svelte'
-  import tracker from '../../plugin'
-  import SprintProjectEditor from './SprintProjectEditor.svelte'
-
-  export let _class: Ref<Class<Doc>>
-  export let itemsConfig: (BuildModelKey | string)[]
-  export let selectedObjectIds: Doc[] = []
-  export let selectedRowIndex: number | undefined = undefined
-  export let sprints: WithLookup<Sprint>[] | undefined = undefined
-  export let loadingProps: LoadingProps | undefined = undefined
-
-  const dispatch = createEventDispatcher()
-
-  const client = getClient()
-  const objectRefs: HTMLElement[] = []
-
-  const baseOptions: FindOptions<Issue> = {
-    lookup: {
-      assignee: contact.class.Employee,
-      status: tracker.class.IssueStatus
-    }
-  }
-
-  $: options = { ...baseOptions } as FindOptions<Sprint>
-  $: selectedObjectIdsSet = new Set<Ref<Doc>>(selectedObjectIds.map((it) => it._id))
-  $: objectRefs.length = sprints?.length ?? 0
-
-  $: byProject = sprints?.reduce((s, cur) => {
-    const pid = cur.project ?? ''
-    s.set(pid, [...(s.get(pid) ?? []), cur])
-    return s
-  }, new Map<Ref<Project> | '', WithLookup<Sprint>[]>())
-
-  export const onObjectChecked = (docs: Doc[], value: boolean) => {
-    dispatch('check', { docs, value })
-  }
-
-  const handleRowFocused = (object: Doc) => {
-    dispatch('row-focus', object)
-  }
-
-  export const onElementSelected = (offset: 1 | -1 | 0, docObject?: Doc) => {
-    if (!sprints) {
-      return
-    }
-
-    let position =
-      (docObject !== undefined ? sprints?.findIndex((x) => x._id === docObject?._id) : selectedRowIndex) ?? -1
-
-    position += offset
-
-    if (position < 0) {
-      position = 0
-    }
-
-    if (position >= sprints.length) {
-      position = sprints.length - 1
-    }
-
-    const objectRef = objectRefs[position]
-
-    selectedRowIndex = position
-
-    handleRowFocused(sprints[position])
-
-    if (objectRef) {
-      objectRef.scrollIntoView({ behavior: 'auto', block: 'nearest' })
-    }
-  }
-
-  const getLoadingElementsLength = (props: LoadingProps, options?: FindOptions<Doc>) => {
-    if (options?.limit && options?.limit > 0) {
-      return Math.min(options.limit, props.length)
-    }
-
-    return props.length
-  }
-
-  const isCollapsedMap: Record<any, boolean> = {}
-
-  $: {
-    const exkeys = new Set(Object.keys(isCollapsedMap))
-    for (const c of byProject?.keys() ?? []) {
-      if (!exkeys.delete(c)) {
-        isCollapsedMap[c] = false
-      }
-    }
-    for (const k of exkeys) {
-      delete isCollapsedMap[k]
-    }
-  }
-
-  const handleCollapseCategory = (category: any) => (isCollapsedMap[category] = !isCollapsedMap[category])
-</script>
-
-{#await buildModel({ client, _class, keys: itemsConfig, lookup: options.lookup }) then itemModels}
-  <div class="listRoot">
-    {#if sprints}
-      {#each Array.from(byProject?.entries() ?? []) as e}
-        <!-- svelte-ignore a11y-click-events-have-key-events -->
-        <div class="flex-between categoryHeader row" on:click={() => handleCollapseCategory(e[0])}>
-          <div class="flex-row-center gap-2 clear-mins">
-            <SprintProjectEditor
-              isEditable={false}
-              value={e[1][0]}
-              enlargedText={true}
-              kind={'list-header'}
-              shouldShowPlaceholder={false}
-            />
-          </div>
-        </div>
-        <ExpandCollapse isExpanded={!isCollapsedMap[e[0]]} duration={400}>
-          {#each e[1] as docObject (docObject._id)}
-            <div
-              bind:this={objectRefs[sprints.findIndex((x) => x === docObject)]}
-              class="listGrid"
-              class:mListGridChecked={selectedObjectIdsSet.has(docObject._id)}
-              class:mListGridFixed={selectedRowIndex === sprints.findIndex((x) => x === docObject)}
-              class:mListGridSelected={selectedRowIndex === sprints.findIndex((x) => x === docObject)}
-              on:focus={() => {}}
-              on:mouseover={() => handleRowFocused(docObject)}
-            >
-              <div class="contentWrapper">
-                {#each itemModels as attributeModel, attributeModelIndex}
-                  {#if attributeModelIndex === 0}
-                    <div class="gridElement">
-                      <div
-                        class="eListGridCheckBox"
-                        use:tooltip={{ direction: 'bottom', label: tracker.string.SelectIssue }}
-                      >
-                        <CheckBox
-                          checked={selectedObjectIdsSet.has(docObject._id)}
-                          on:value={(event) => {
-                            onObjectChecked([docObject], event.detail)
-                          }}
-                        />
-                      </div>
-                      <div class="iconPresenter">
-                        <svelte:component
-                          this={attributeModel.presenter}
-                          value={getObjectValue(attributeModel.key, docObject) ?? ''}
-                          {...attributeModel.props}
-                        />
-                      </div>
-                    </div>
-                  {:else if attributeModelIndex === 1}
-                    <div class="projectPresenter flex-grow">
-                      <svelte:component
-                        this={attributeModel.presenter}
-                        value={getObjectValue(attributeModel.key, docObject) ?? ''}
-                        {...attributeModel.props}
-                      />
-                    </div>
-                    <div class="filler" />
-                  {:else}
-                    <div class="gridElement">
-                      <svelte:component
-                        this={attributeModel.presenter}
-                        value={getObjectValue(attributeModel.key, docObject) ?? ''}
-                        parentId={docObject._id}
-                        sprintId={docObject._id}
-                        {...attributeModel.props}
-                      />
-                    </div>
-                  {/if}
-                {/each}
-              </div>
-            </div>
-          {/each}
-        </ExpandCollapse>
-      {/each}
-    {:else if loadingProps !== undefined}
-      {#each Array(getLoadingElementsLength(loadingProps, options)) as _, rowIndex}
-        <div class="listGrid" class:fixed={rowIndex === selectedRowIndex}>
-          <div class="contentWrapper">
-            <div class="gridElement">
-              <CheckBox checked={false} />
-              <div class="ml-4">
-                <Spinner size="small" />
-              </div>
-            </div>
-          </div>
-        </div>
-      {/each}
-    {/if}
-  </div>
-{/await}
-
-<style lang="scss">
-  .listRoot {
-    width: 100%;
-  }
-
-  .categoryHeader {
-    position: sticky;
-    top: 0;
-    padding: 0 1.5rem 0 2.25rem;
-    height: 3rem;
-    min-height: 3rem;
-    min-width: 0;
-    background-color: var(--accent-bg-color);
-    z-index: 5;
-  }
-
-  .contentWrapper {
-    display: flex;
-    align-items: center;
-    height: 100%;
-    padding-left: 0.75rem;
-    padding-right: 1.15rem;
-  }
-
-  .listGrid {
-    width: 100%;
-    height: 3.25rem;
-    color: var(--theme-caption-color);
-    border-bottom: 1px solid var(--theme-button-border-hovered);
-
-    &.mListGridChecked {
-      background-color: var(--theme-table-bg-hover);
-
-      .eListGridCheckBox {
-        opacity: 1;
-      }
-    }
-
-    &.mListGridSelected {
-      background-color: var(--menu-bg-select);
-    }
-
-    .eListGridCheckBox {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      opacity: 0;
-
-      &:hover {
-        opacity: 1;
-      }
-    }
-  }
-
-  .filler {
-    display: flex;
-    flex-grow: 1;
-  }
-
-  .gridElement {
-    display: flex;
-    align-items: center;
-    justify-content: flex-start;
-    margin-left: 0.5rem;
-
-    &:first-child {
-      margin-left: 0;
-    }
-  }
-
-  .iconPresenter {
-    padding-left: 0.45rem;
-  }
-
-  .projectPresenter {
-    display: flex;
-    align-items: center;
-    flex-shrink: 0;
-    width: 5.5rem;
-    margin-left: 0.5rem;
-  }
-</style>
diff --git a/plugins/tracker-resources/src/components/sprints/SprintListBrowser.svelte b/plugins/tracker-resources/src/components/sprints/SprintListBrowser.svelte
deleted file mode 100644
index f40b1f5478..0000000000
--- a/plugins/tracker-resources/src/components/sprints/SprintListBrowser.svelte
+++ /dev/null
@@ -1,72 +0,0 @@
-<!--
-// Copyright © 2022 Hardcore Engineering Inc.
-// 
-// Licensed under the Eclipse Public License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License. You may
-// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
-// 
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// 
-// See the License for the specific language governing permissions and
-// limitations under the License.
--->
-<script lang="ts">
-  import type { Class, Doc, Ref, WithLookup } from '@hcengineering/core'
-  import { Sprint } from '@hcengineering/tracker'
-  import { BuildModelKey } from '@hcengineering/view'
-  import {
-    ActionContext,
-    focusStore,
-    ListSelectionProvider,
-    LoadingProps,
-    SelectDirection,
-    selectionStore
-  } from '@hcengineering/view-resources'
-  import { onMount } from 'svelte'
-  import SprintList from './SprintList.svelte'
-
-  export let _class: Ref<Class<Doc>>
-  export let itemsConfig: (BuildModelKey | string)[]
-  export let loadingProps: LoadingProps | undefined = undefined
-  export let sprints: WithLookup<Sprint>[] = []
-
-  const listProvider = new ListSelectionProvider((offset: 1 | -1 | 0, of?: Doc, dir?: SelectDirection) => {
-    if (dir === 'vertical') {
-      sprintsList.onElementSelected(offset, of)
-    }
-  })
-
-  let sprintsList: SprintList
-
-  $: if (sprintsList !== undefined) {
-    listProvider.update(sprints)
-  }
-
-  onMount(() => {
-    ;(document.activeElement as HTMLElement)?.blur()
-  })
-</script>
-
-<ActionContext
-  context={{
-    mode: 'browser'
-  }}
-/>
-
-<SprintList
-  bind:this={sprintsList}
-  {_class}
-  {itemsConfig}
-  {loadingProps}
-  {sprints}
-  selectedObjectIds={$selectionStore ?? []}
-  selectedRowIndex={listProvider.current($focusStore)}
-  on:row-focus={(event) => {
-    listProvider.updateFocus(event.detail ?? undefined)
-  }}
-  on:check={(event) => {
-    listProvider.updateSelection(event.detail.docs, event.detail.value)
-  }}
-/>
diff --git a/plugins/tracker-resources/src/index.ts b/plugins/tracker-resources/src/index.ts
index 0a47de049b..95c61a8c82 100644
--- a/plugins/tracker-resources/src/index.ts
+++ b/plugins/tracker-resources/src/index.ts
@@ -70,6 +70,10 @@ import Views from './components/views/Views.svelte'
 import Statuses from './components/workflow/Statuses.svelte'
 import RelatedIssuesSection from './components/issues/related/RelatedIssuesSection.svelte'
 import RelatedIssueSelector from './components/issues/related/RelatedIssueSelector.svelte'
+import SprintProjectEditor from './components/sprints/SprintProjectEditor.svelte'
+import SprintDatePresenter from './components/sprints/SprintDatePresenter.svelte'
+import SprintLeadPresenter from './components/sprints/SprintLeadPresenter.svelte'
+
 import {
   getIssueId,
   getIssueTitle,
@@ -380,7 +384,10 @@ export default async (): Promise<Resources> => ({
     RelatedIssuesSection,
     RelatedIssueSelector,
     DeleteProjectPresenter,
-    TimeSpendReportPopup
+    TimeSpendReportPopup,
+    SprintProjectEditor,
+    SprintDatePresenter,
+    SprintLeadPresenter
   },
   completion: {
     IssueQuery: async (client: Client, query: string, filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] }) =>
diff --git a/plugins/tracker-resources/src/plugin.ts b/plugins/tracker-resources/src/plugin.ts
index ab5cb6b03b..5376b0f253 100644
--- a/plugins/tracker-resources/src/plugin.ts
+++ b/plugins/tracker-resources/src/plugin.ts
@@ -350,6 +350,9 @@ export default mergeIds(trackerId, tracker, {
     SprintPresenter: '' as AnyComponent,
     SprintStatusPresenter: '' as AnyComponent,
     SprintTitlePresenter: '' as AnyComponent,
+    SprintProjectEditor: '' as AnyComponent,
+    SprintDatePresenter: '' as AnyComponent,
+    SprintLeadPresenter: '' as AnyComponent,
     ReportedTimeEditor: '' as AnyComponent,
     TimeSpendReport: '' as AnyComponent,
     EstimationEditor: '' as AnyComponent,
diff --git a/plugins/view-resources/src/index.ts b/plugins/view-resources/src/index.ts
index c2a8bc31bc..2b2a9b86cb 100644
--- a/plugins/view-resources/src/index.ts
+++ b/plugins/view-resources/src/index.ts
@@ -98,6 +98,7 @@ export { default as ObjectPresenter } from './components/ObjectPresenter.svelte'
 export { default as TableBrowser } from './components/TableBrowser.svelte'
 export { default as ValueSelector } from './components/ValueSelector.svelte'
 export { default as MarkupPreviewPopup } from './components/MarkupPreviewPopup.svelte'
+export { default as MarkupPresenter } from './components/MarkupPresenter.svelte'
 export * from './context'
 export * from './filter'
 export * from './selection'